Fix use-after-free in rpc test response helper (#5166) * Fix use-after-free in rpc test response helper `test_response` chained async_connect -> async_write -> async_read with handlers capturing a raw `this`, while callers held it by value or unique_ptr. Nothing tied the object's lifetime to the operations it had started, so dropping the caller's handle mid-flight left queued handlers pointing at freed memory. A failing ASSERT_TIMELY inside `wait_response_impl` is exactly that path, since it expands to a bare `return`. The macOS TSAN CI job hits this as a SEGV during `~system()`: the RPC request times out, the stack allocated `test_response` is destroyed, and the pending write completion then starts a read on the destroyed socket while the teardown poll loop drains the io_context. Make `test_response` shared and give every handler a lifetime guard. Construction goes through `prepare`/`send`, so stack allocating one - the shape that caused the bug - no longer compiles. `request` becomes a value copy, as the object can now legitimately outlive the caller's ptree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Join worker threads in rpc simultaneous_calls The worker threads were detached while capturing `test_responses`, `promise` and `count` by reference. A failing assertion returns from the test body and destroys those stack objects, so any assertion firing while the threads are still running left them writing into dead memory - the same defect as the response helper, one scope up. Injecting an early assertion failure and running under ASAN reports `stack-use-after-scope` in the `--count` decrement. `std::jthread` would cover this, but libc++ on macOS does not provide it, so add a `join_guard` next to the other test guards and join explicitly. It is declared after the objects the threads capture, so it is destroyed first and joins before they go away. Joining right after the spawn loop instead would be simpler, but this test exists to exercise threading issues under TSAN, which relies on the io_context being pumped by the `ASSERT_TIMELY` below while the workers are still calling `run`. The guard keeps that overlap and only changes the failure path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
__________
Type: commit
Hash: a1ccec51a113f95e8adedff9595c7acc6e11364b
Created: yesterday
__________
gr0v1ty/nano-node:a1ccec51a113f95e8adedff9595c7acc6e11364b
| Test Case | Status | Duration | Median Deviation | Log | Graph |
|---|