mptest: mp::CancelMonitor: AddressSanitizer: stack-use-after-return #34782

issue maflcko openend this issue on March 10, 2026
  1. maflcko commented at 8:37 am on March 10, 2026: member

    https://github.com/bitcoin/bitcoin/actions/runs/22735105396/job/65934330181?pr=33506#step:11:2119

      0  3/157 Test   [#3](/bitcoin-bitcoin/3/): mptest ...............................***Failed    0.18 sec
      1[ TEST ] spawn_tests.cpp:44: SpawnProcess does not run callback in child
      2[ PASS ] spawn_tests.cpp:44: SpawnProcess does not run callback in child (56334 μs)
      3[ TEST ] test.cpp:124: Call FooInterface methods
      4[ PASS ] test.cpp:124: Call FooInterface methods (17341 μs)
      5[ TEST ] test.cpp:216: Call IPC method after client connection is closed
      6[ PASS ] test.cpp:216: Call IPC method after client connection is closed (1254 μs)
      7[ TEST ] test.cpp:233: Calling IPC method after server connection is closed
      8[ PASS ] test.cpp:233: Calling IPC method after server connection is closed (1021 μs)
      9[ TEST ] test.cpp:250: Calling IPC method and disconnecting during the call
     10[ PASS ] test.cpp:250: Calling IPC method and disconnecting during the call (4656 μs)
     11[ TEST ] test.cpp:270: Calling IPC method, disconnecting and blocking during the call
     12=================================================================
     13==7734==ERROR: AddressSanitizer: stack-use-after-return on address 0x6feb59170448 at pc 0x61f7c97c8d38 bp 0x73eb5c1ef890 sp 0x73eb5c1ef888
     14WRITE of size 1 at 0x6feb59170448 thread T12
     15    [#0](/bitcoin-bitcoin/0/) 0x61f7c97c8d37 in std::enable_if<std::is_same<decltype(mp::Accessor<mp::foo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>, mp::ServerCall, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&, mp::ServerCall const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda'()::operator()() const::'lambda'()::operator()() const /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/include/mp/type-context.h:127:61
     16    [#1](/bitcoin-bitcoin/1/) 0x61f7c96eb5f7 in std::function<void ()>::operator()() const /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_function.h:591:9
     17    [#2](/bitcoin-bitcoin/2/) 0x61f7c96eb5f7 in mp::CancelMonitor::promiseDestroyed(mp::CancelProbe&) /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/include/mp/util.h:337:22
     18    [#3](/bitcoin-bitcoin/3/) 0x61f7c96eb5f7 in mp::CancelProbe::~CancelProbe() /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/include/mp/util.h:314:35
     19    [#4](/bitcoin-bitcoin/4/) 0x61f7c96eb49b in kj::_::HeapDisposer<mp::CancelProbe>::disposeImpl(void*) const /usr/include/kj/memory.h:557:60
     20    [#5](/bitcoin-bitcoin/5/) 0x61f7c96eb222 in kj::Disposer::Dispose_<mp::CancelProbe, false>::dispose(mp::CancelProbe*, kj::Disposer const&) /usr/include/kj/memory.h:675:14
     21    [#6](/bitcoin-bitcoin/6/) 0x61f7c96eb222 in void kj::Disposer::dispose<mp::CancelProbe>(mp::CancelProbe*) const /usr/include/kj/memory.h:681:3
     22    [#7](/bitcoin-bitcoin/7/) 0x61f7c96eb222 in kj::Own<mp::CancelProbe, std::nullptr_t>::dispose() /usr/include/kj/memory.h:284:17
     23    [#8](/bitcoin-bitcoin/8/) 0x61f7c96eb222 in kj::Own<mp::CancelProbe, std::nullptr_t>::~Own() /usr/include/kj/memory.h:210:28
     24    [#9](/bitcoin-bitcoin/9/) 0x61f7c96eb222 in kj::_::AttachmentPromiseNode<kj::Own<mp::CancelProbe, std::nullptr_t>>::~AttachmentPromiseNode() /usr/include/kj/async-inl.h:549:3
     25    [#10](/bitcoin-bitcoin/10/) 0x73eb5d1fd5b9 in kj::_::TransformPromiseNodeBase::dropDependency() (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x3e5b9) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     26    [#11](/bitcoin-bitcoin/11/) 0x61f7c97d36fc in kj::_::TransformPromiseNode<kj::_::Void, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>, kj::Canceler::AdapterImpl<capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>::AdapterImpl(kj::PromiseFulfiller<capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&, kj::Canceler&, kj::Promise<capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>)::'lambda'(capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>&&), kj::Canceler::AdapterImpl<capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>::AdapterImpl(kj::PromiseFulfiller<capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&, kj::Canceler&, kj::Promise<capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>)::'lambda'(kj::Exception&&)>::~TransformPromiseNode() /usr/include/kj/async-inl.h:724:5
     27    [#12](/bitcoin-bitcoin/12/) 0x61f7c9514672 in kj::_::PromiseDisposer::dispose(kj::_::PromiseArenaMember*) /usr/include/kj/async-inl.h:354:11
     28    [#13](/bitcoin-bitcoin/13/) 0x61f7c96f1063 in kj::Own<kj::_::PromiseNode, kj::_::PromiseDisposer>::dispose() /usr/include/kj/memory.h:398:7
     29    [#14](/bitcoin-bitcoin/14/) 0x61f7c96f1063 in kj::Own<kj::_::PromiseNode, kj::_::PromiseDisposer>::~Own() /usr/include/kj/memory.h:337:28
     30    [#15](/bitcoin-bitcoin/15/) 0x61f7c96f1063 in kj::_::EagerPromiseNodeBase::~EagerPromiseNodeBase() /usr/include/kj/async-inl.h:1106:7
     31    [#16](/bitcoin-bitcoin/16/) 0x61f7c96f1063 in kj::_::EagerPromiseNode<kj::_::Void>::~EagerPromiseNode() /usr/include/kj/async-inl.h:1128:7
     32    [#17](/bitcoin-bitcoin/17/) 0x61f7c9514672 in kj::_::PromiseDisposer::dispose(kj::_::PromiseArenaMember*) /usr/include/kj/async-inl.h:354:11
     33    [#18](/bitcoin-bitcoin/18/) 0x61f7c97d2433 in kj::Own<kj::_::PromiseNode, kj::_::PromiseDisposer>::operator=(kj::Own<kj::_::PromiseNode, kj::_::PromiseDisposer>&&) /usr/include/kj/memory.h:348:7
     34    [#19](/bitcoin-bitcoin/19/) 0x61f7c97d2433 in kj::_::PromiseBase::operator=(kj::_::PromiseBase&&) /usr/include/kj/async-prelude.h:222:7
     35    [#20](/bitcoin-bitcoin/20/) 0x61f7c97d2433 in kj::Promise<void>::operator=(kj::Promise<void>&&) /usr/include/kj/async.h:121:7
     36    [#21](/bitcoin-bitcoin/21/) 0x61f7c97d2433 in kj::Canceler::AdapterImpl<capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>::cancel(kj::Exception&&) /usr/include/kj/async.h:943:13
     37    [#22](/bitcoin-bitcoin/22/) 0x73eb5d1f2251 in kj::Canceler::cancel(kj::Exception const&) (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x33251) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     38    [#23](/bitcoin-bitcoin/23/) 0x73eb5d1f23e5 in kj::Canceler::cancel(kj::StringPtr) (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x333e5) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     39    [#24](/bitcoin-bitcoin/24/) 0x61f7c98b84d1 in mp::Connection::~Connection() /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/src/mp/proxy.cpp:92:16
     40    [#25](/bitcoin-bitcoin/25/) 0x61f7c9515abe in std::default_delete<mp::Connection>::operator()(mp::Connection*) const /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/unique_ptr.h:99:2
     41    [#26](/bitcoin-bitcoin/26/) 0x61f7c9515abe in std::__uniq_ptr_impl<mp::Connection, std::default_delete<mp::Connection>>::reset(mp::Connection*) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/unique_ptr.h:211:4
     42    [#27](/bitcoin-bitcoin/27/) 0x61f7c9515abe in std::unique_ptr<mp::Connection, std::default_delete<mp::Connection>>::reset(mp::Connection*) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/unique_ptr.h:509:7
     43    [#28](/bitcoin-bitcoin/28/) 0x61f7c9515abe in mp::test::TestSetup::TestSetup(bool)::'lambda'()::operator()() const::'lambda0'()::operator()() const /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/test/mp/test/test.cpp:93:71
     44    [#29](/bitcoin-bitcoin/29/) 0x61f7c9515abe in kj::_::Void kj::_::MaybeVoidCaller<kj::_::Void, kj::_::Void>::apply<mp::test::TestSetup::TestSetup(bool)::'lambda'()::operator()() const::'lambda0'()>(mp::test::TestSetup::TestSetup(bool)::'lambda'()::operator()() const::'lambda0'()&, kj::_::Void&&) /usr/include/kj/async-prelude.h:195:5
     45    [#30](/bitcoin-bitcoin/30/) 0x61f7c9515abe in kj::_::TransformPromiseNode<kj::_::Void, kj::_::Void, mp::test::TestSetup::TestSetup(bool)::'lambda'()::operator()() const::'lambda0'(), kj::_::PropagateException>::getImpl(kj::_::ExceptionOrValue&) /usr/include/kj/async-inl.h:739:31
     46    [#31](/bitcoin-bitcoin/31/) 0x73eb5d1fd61c in kj::_::TransformPromiseNodeBase::get(kj::_::ExceptionOrValue&) (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x3e61c) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     47    [#32](/bitcoin-bitcoin/32/) 0x73eb5d1f92e6  (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x3a2e6) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     48    [#33](/bitcoin-bitcoin/33/) 0x73eb5d1f4508 in kj::EventLoop::turn() (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x35508) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     49    [#34](/bitcoin-bitcoin/34/) 0x73eb5d1fa6c8 in kj::_::waitImpl(kj::Own<kj::_::PromiseNode, kj::_::PromiseDisposer>&&, kj::_::ExceptionOrValue&, kj::WaitScope&, kj::SourceLocation) (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x3b6c8) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     50    [#35](/bitcoin-bitcoin/35/) 0x61f7c98c50a4 in kj::Promise<unsigned long>::wait(kj::WaitScope&, kj::SourceLocation) /usr/include/kj/async-inl.h:1357:3
     51    [#36](/bitcoin-bitcoin/36/) 0x61f7c98bb472 in mp::EventLoop::loop() /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/src/mp/proxy.cpp:244:68
     52    [#37](/bitcoin-bitcoin/37/) 0x61f7c950cedf in mp::test::TestSetup::TestSetup(bool)::'lambda'()::operator()() const /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/test/mp/test/test.cpp:106:20
     53    [#38](/bitcoin-bitcoin/38/) 0x73eb5cfaddb3  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xecdb3) (BuildId: 753c6c8608b61d4e67be8f0c890e03e0aa046b8b)
     54    [#39](/bitcoin-bitcoin/39/) 0x61f7c94aa78a in asan_thread_start(void*) crtstuff.c
     55    [#40](/bitcoin-bitcoin/40/) 0x73eb5cc21aa3  (/lib/x86_64-linux-gnu/libc.so.6+0x9caa3) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
     56    [#41](/bitcoin-bitcoin/41/) 0x73eb5ccaec6b  (/lib/x86_64-linux-gnu/libc.so.6+0x129c6b) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
     57
     58Address 0x6feb59170448 is located in stack of thread T13 at offset 1096 in frame
     59    [#0](/bitcoin-bitcoin/0/) 0x61f7c97c4f3f in std::enable_if<std::is_same<decltype(mp::Accessor<mp::foo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>, mp::ServerCall, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&, mp::ServerCall const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&) /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/include/mp/type-context.h:75
     60
     61  This frame has 21 object(s):
     62    [32, 56) 'ref.tmp'
     63    [96, 144) 'agg.tmp'
     64    [176, 208) 'agg.tmp8.i.i'
     65    [240, 288) 'agg.tmp'
     66    [320, 352) 'agg.tmp4.i.i'
     67    [384, 416) 'ref.tmp'
     68    [448, 840) 'logger' (line 76)
     69    [912, 944) 'ref.tmp' (line 76)
     70    [976, 1024) 'context_arg' (line 78)
     71    [1056, 1104) 'server_context' (line 79) <== Memory access at offset 1096 is inside this variable
     72    [1136, 1144) 'request_thread' (line 100)
     73    [1168, 1169) 'inserted' (line 101)
     74    [1184, 1224) 'cancel_mutex' (line 102)
     75    [1264, 1280) 'cancel_lock' (line 103)
     76    [1296, 1297) 'erase_thread' (line 140)
     77    [1312, 1704) 'logger' (line 173)
     78    [1776, 1808) 'ref.tmp' (line 173)
     79    [1840, 2216) 'exception' (line 174)
     80    [2288, 2664) 'ref.tmp' (line 174)
     81    [2736, 3128) 'logger' (line 181)
     82    [3200, 3232) 'ref.tmp' (line 181)
     83HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
     84      (longjmp and C++ exceptions *are* supported)
     85Thread T13 created by T12 here:
     86    [#0](/bitcoin-bitcoin/0/) 0x61f7c9490315 in pthread_create (/home/admin/actions-runner/_work/_temp/build/src/ipc/libmultiprocess/test/mptest+0x29f315) (BuildId: f1db639df98247caaf2b3684794345120950dffa)
     87    [#1](/bitcoin-bitcoin/1/) 0x73eb5cfadeb0 in std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) (/lib/x86_64-linux-gnu/libstdc++.so.6+0xeceb0) (BuildId: 753c6c8608b61d4e67be8f0c890e03e0aa046b8b)
     88    [#2](/bitcoin-bitcoin/2/) 0x61f7c98bfe10 in std::thread::thread<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0, void>(mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0&&) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:164:2
     89    [#3](/bitcoin-bitcoin/3/) 0x61f7c98bfe10 in mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>) /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/src/mp/proxy.cpp:416:17
     90    [#4](/bitcoin-bitcoin/4/) 0x61f7c98b54fb in mp::ThreadMap::Server::dispatchCallInternal(unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/admin/actions-runner/_work/_temp/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++:588:9
     91    [#5](/bitcoin-bitcoin/5/) 0x61f7c98b54fb in mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/admin/actions-runner/_work/_temp/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++:577:14
     92    [#6](/bitcoin-bitcoin/6/) 0x61f7c98b54fb in virtual thunk to mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/admin/actions-runner/_work/_temp/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++
     93    [#7](/bitcoin-bitcoin/7/) 0x73eb5d37c9e5  (/lib/x86_64-linux-gnu/libcapnp-rpc-1.0.1.so+0x5b9e5) (BuildId: 44717f0ad52d0e372420b8de2d7701bfba298f14)
     94    [#8](/bitcoin-bitcoin/8/) 0x73eb5d1fd61c in kj::_::TransformPromiseNodeBase::get(kj::_::ExceptionOrValue&) (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x3e61c) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     95    [#9](/bitcoin-bitcoin/9/) 0x73eb5d1fe45c in kj::_::ChainPromiseNode::fire() (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x3f45c) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     96    [#10](/bitcoin-bitcoin/10/) 0x73eb5d1f4508 in kj::EventLoop::turn() (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x35508) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     97    [#11](/bitcoin-bitcoin/11/) 0x73eb5d1fa6c8 in kj::_::waitImpl(kj::Own<kj::_::PromiseNode, kj::_::PromiseDisposer>&&, kj::_::ExceptionOrValue&, kj::WaitScope&, kj::SourceLocation) (/lib/x86_64-linux-gnu/libkj-async-1.0.1.so+0x3b6c8) (BuildId: 356b68ed6bfbcc99793859e96d335850186ca5da)
     98    [#12](/bitcoin-bitcoin/12/) 0x61f7c98c50a4 in kj::Promise<unsigned long>::wait(kj::WaitScope&, kj::SourceLocation) /usr/include/kj/async-inl.h:1357:3
     99    [#13](/bitcoin-bitcoin/13/) 0x61f7c98bb472 in mp::EventLoop::loop() /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/src/mp/proxy.cpp:244:68
    100    [#14](/bitcoin-bitcoin/14/) 0x61f7c950cedf in mp::test::TestSetup::TestSetup(bool)::'lambda'()::operator()() const /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/test/mp/test/test.cpp:106:20
    101    [#15](/bitcoin-bitcoin/15/) 0x73eb5cfaddb3  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xecdb3) (BuildId: 753c6c8608b61d4e67be8f0c890e03e0aa046b8b)
    102    [#16](/bitcoin-bitcoin/16/) 0x61f7c94aa78a in asan_thread_start(void*) crtstuff.c
    103
    104Thread T12 created by T0 here:
    105    [#0](/bitcoin-bitcoin/0/) 0x61f7c9490315 in pthread_create (/home/admin/actions-runner/_work/_temp/build/src/ipc/libmultiprocess/test/mptest+0x29f315) (BuildId: f1db639df98247caaf2b3684794345120950dffa)
    106    [#1](/bitcoin-bitcoin/1/) 0x73eb5cfadeb0 in std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) (/lib/x86_64-linux-gnu/libstdc++.so.6+0xeceb0) (BuildId: 753c6c8608b61d4e67be8f0c890e03e0aa046b8b)
    107    [#2](/bitcoin-bitcoin/2/) 0x61f7c950b044 in std::thread::thread<mp::test::TestSetup::TestSetup(bool)::'lambda'(), void>(mp::test::TestSetup::TestSetup(bool)::'lambda'()&&) /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_thread.h:164:2
    108    [#3](/bitcoin-bitcoin/3/) 0x61f7c9501b7e in mp::test::TestSetup::TestSetup(bool) /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/test/mp/test/test.cpp:75:11
    109    [#4](/bitcoin-bitcoin/4/) 0x61f7c94fc159 in mp::test::TestCase270::run() /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/test/mp/test/test.cpp:291:15
    110    [#5](/bitcoin-bitcoin/5/) 0x73eb5d4070e1  (/lib/x86_64-linux-gnu/libkj-test-1.0.1.so+0x50e1) (BuildId: 2ff7f524274168e50347a2d6dd423f273ae8d268)
    111    [#6](/bitcoin-bitcoin/6/) 0x73eb5d407657  (/lib/x86_64-linux-gnu/libkj-test-1.0.1.so+0x5657) (BuildId: 2ff7f524274168e50347a2d6dd423f273ae8d268)
    112    [#7](/bitcoin-bitcoin/7/) 0x73eb5d19637f in kj::MainBuilder::MainImpl::operator()(kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>) (/lib/x86_64-linux-gnu/libkj-1.0.1.so+0x5537f) (BuildId: 4b52c0e2756bcb53e58e705fcb10bab8f63f24fd)
    113    [#8](/bitcoin-bitcoin/8/) 0x73eb5d191499 in kj::runMainAndExit(kj::ProcessContext&, kj::Function<void (kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>)>&&, int, char**) (/lib/x86_64-linux-gnu/libkj-1.0.1.so+0x50499) (BuildId: 4b52c0e2756bcb53e58e705fcb10bab8f63f24fd)
    114    [#9](/bitcoin-bitcoin/9/) 0x73eb5d405fcb in main (/lib/x86_64-linux-gnu/libkj-test-1.0.1.so+0x3fcb) (BuildId: 2ff7f524274168e50347a2d6dd423f273ae8d268)
    115    [#10](/bitcoin-bitcoin/10/) 0x73eb5cbaf1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    116    [#11](/bitcoin-bitcoin/11/) 0x73eb5cbaf28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    117    [#12](/bitcoin-bitcoin/12/) 0x61f7c9406ce4 in _start (/home/admin/actions-runner/_work/_temp/build/src/ipc/libmultiprocess/test/mptest+0x215ce4) (BuildId: f1db639df98247caaf2b3684794345120950dffa)
    118
    119SUMMARY: AddressSanitizer: stack-use-after-return /home/admin/actions-runner/_work/_temp/src/ipc/libmultiprocess/include/mp/type-context.h:127:61 in std::enable_if<std::is_same<decltype(mp::Accessor<mp::foo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>, mp::ServerCall, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&, mp::ServerCall const&, mp::TypeList<>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda'()::operator()() const::'lambda'()::operator()() const
    120Shadow bytes around the buggy address:
    121  0x6feb59170180: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    122  0x6feb59170200: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    123  0x6feb59170280: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    124  0x6feb59170300: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    125  0x6feb59170380: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    126=>0x6feb59170400: f5 f5 f5 f5 f5 f5 f5 f5 f5[f5]f5 f5 f5 f5 f5 f5
    127  0x6feb59170480: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    128  0x6feb59170500: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    129  0x6feb59170580: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    130  0x6feb59170600: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    131  0x6feb59170680: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
    132Shadow byte legend (one shadow byte represents 8 application bytes):
    133  Addressable:           00
    134  Partially addressable: 01 02 03 04 05 06 07 
    135  Heap left redzone:       fa
    136  Freed heap region:       fd
    137  Stack left redzone:      f1
    138  Stack mid redzone:       f2
    139  Stack right redzone:     f3
    140  Stack after return:      f5
    141  Stack use after scope:   f8
    142  Global redzone:          f9
    143  Global init order:       f6
    144  Poisoned by user:        f7
    145  Container overflow:      fc
    146  Array cookie:            ac
    147  Intra object redzone:    bb
    148  ASan internal:           fe
    149  Left alloca redzone:     ca
    150  Right alloca redzone:    cb
    151==7734==ABORTING
    152
    153  4/157 Test  [#10](/bitcoin-bitcoin/10/): allocator_tests ......................   Passed    0.18 sec
    154  5/157 Test   [#9](/bitcoin-bitcoin/9/): addrman_tests ........................   Passed    0.91 sec
    155  6/157 Test   [#8](/bitcoin-bitcoin/8/): test_kernel ..........................   Passed    1.54 sec
    156  7/157 Test   [#7](/bitcoin-bitcoin/7/): test_bitcoin-qt ......................   Passed    7.19 sec
    157  8/157 Test   [#6](/bitcoin-bitcoin/6/): secp256k1_exhaustive_tests ...........   Passed   26.30 sec
    158  9/157 Test   [#4](/bitcoin-bitcoin/4/): secp256k1_noverify_tests .............   Passed  102.35 sec
    159 10/157 Test   [#5](/bitcoin-bitcoin/5/): secp256k1_tests ......................   Passed  212.62 sec
    160
    16190% tests passed, 1 tests failed out of 10
    162
    163Total Test time (real) = 212.63 sec
    164
    165The following tests FAILED:
    166	  3 - mptest (Failed)
    167Errors while running CTest
    168Command '['docker', 'exec', 'c876f21a9950bbc606c88d98bccd5c003d3940433a5dc6759363b61c47472caa', '/home/admin/actions-runner/_work/_temp/ci/test/03_test_script.sh']' returned non-zero exit status 8.
    169Error: Process completed with exit code 1.
    
  2. maflcko added the label CI failed on Mar 10, 2026
  3. maflcko added this to the milestone 31.0 on Mar 10, 2026
  4. fanquake commented at 10:45 am on March 10, 2026: member
  5. dergoegge commented at 12:52 pm on March 10, 2026: member
    I’ve also seen this happen with a test on Antithesis. I’m assuming the unit tests will be easier to debug, but lmk if there is interest in the Antithesis logs as well.
  6. ryanofsky commented at 2:09 pm on March 10, 2026: contributor

    I’ve also seen this happen with a test on Antithesis. I’m assuming the unit tests will be easier to debug, but lmk if there is interest in the Antithesis logs as well.

    Thanks, helpful to know this is not just a test bug. I think it’s probably right that test should be easier to debug for now but I can let you know.

  7. ryanofsky commented at 5:24 pm on March 10, 2026: contributor

    Bug is clear from the stack trace. Cancellation happens in the connection destructor:

    https://github.com/bitcoin/bitcoin/blob/f82d07677136dbc60ebbb2fd0cb11df074fd97b2/src/ipc/libmultiprocess/src/mp/proxy.cpp#L92

    and it triggers stack-use-after-return error in the m_on_cancel callback which is trying to set a local server_request.request_canceled variable:

    https://github.com/bitcoin/bitcoin/blob/f82d07677136dbc60ebbb2fd0cb11df074fd97b2/src/ipc/libmultiprocess/include/mp/type-context.h#L127

    This condition happens rarely because normally the cancel_monitor is destroyed right after server_context goes output scope, so there isn’t very much time for it a cancellation to trigger and make an invalid stack access, but this failure shows it is possible.

    A minimal fix should be:

     0--- a/src/ipc/libmultiprocess/include/mp/type-context.h
     1+++ b/src/ipc/libmultiprocess/include/mp/type-context.h
     2@@ -153,6 +153,15 @@ auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn&
     3                         // the disconnect handler trying to destroy the thread
     4                         // client object.
     5                         server.m_context.loop->sync([&] {
     6+                            // Clear cancellation callback. At this point the
     7+                            // method invocation finished and the result is
     8+                            // either being returned, or discarded if a
     9+                            // cancellation happened. So we do not need to be
    10+                            // notified of cancellations after this point. Also
    11+                            // we do not want to be notified because
    12+                            // cancel_mutex and server_context could be out of
    13+                            // scope when it happens.
    14+                            cancel_monitor.m_on_cancel = nullptr;
    15                             auto self_dispose{kj::mv(self)};
    16                             if (erase_thread) {
    17                             // Look up the thread again without using existing
    

    Seeing a third new disconnect race bug like this (in addition #34777 and #34711/#34756) is discouraging but I think there are practical steps that can be taken to prevent more occurrences.

    For a few days in September I struggled to write comprehensive disconnect unit tests that would try to exercise most code paths and shutdown sequences, but got bogged down in the complexity of that. Maybe there is a way to revive that change or a more limited version of it.

    A more promising approach might just be to add fuzzing to libmultiprocess CI and write fuzz tests. Right now libmultiprocess doesn’t have any at all and that seems like an obvious thing to correct.

    In retrospect I guess it’s also not surprising these new race conditions are being found because https://github.com/bitcoin-core/libmultiprocess/pull/240 was just merged, and that PR was my first-ever attempt to use capnproto’s cancellation features so I’m just learning about patterns of bugs that are possible with cancellation now

  8. Sjors commented at 5:54 pm on March 10, 2026: member

    Here’s a test. It builds on the fix in #34777, the test I wrote in #34777 (comment) and your fix here.

     0diff --git a/src/ipc/libmultiprocess/include/mp/type-context.h b/src/ipc/libmultiprocess/include/mp/type-context.h
     1index 34f4aa41f9..88c9f64af6 100644
     2--- a/src/ipc/libmultiprocess/include/mp/type-context.h
     3+++ b/src/ipc/libmultiprocess/include/mp/type-context.h
     4@@ -191,10 +191,13 @@ auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn&
     5                         MP_LOG(*server.m_context.loop, Log::Error) << "IPC server request #" << req << " uncaught exception (" << kj::str(*exception).cStr() << ")";
     6                         throw kj::mv(*exception);
     7                     }
     8                     // End of scope: if KJ_DEFER was reached, it runs here
     9                 }
    10+#ifndef NDEBUG
    11+                if (server.m_context.testing_hook_after_cleanup) server.m_context.testing_hook_after_cleanup();
    12+#endif
    13                 return call_context;
    14             };
    15
    16     // Lookup Thread object specified by the client. The specified thread should
    17     // be a local Thread::Server object, but it needs to be looked up
    18diff --git a/src/ipc/libmultiprocess/test/mp/test/test.cpp b/src/ipc/libmultiprocess/test/mp/test/test.cpp
    19index 8b776b83ea..fc763eb5c4 100644
    20--- a/src/ipc/libmultiprocess/test/mp/test/test.cpp
    21+++ b/src/ipc/libmultiprocess/test/mp/test/test.cpp
    22@@ -355,10 +355,43 @@ KJ_TEST("Calling async IPC method, with server disconnect racing the call")
    23     }
    24     disconnect_thread.join();
    25 #endif
    26 }
    27
    28+KJ_TEST("Calling async IPC method, with server disconnect after cleanup")
    29+{
    30+#ifdef NDEBUG
    31+    KJ_LOG(WARNING, "Test skipped: testing_hook_after_cleanup requires debug build");
    32+    return;
    33+#else
    34+    // Regression test for bitcoin/bitcoin#34782 (stack-use-after-return where
    35+    // the m_on_cancel callback accessed stack-local cancel_mutex and
    36+    // server_context after the invoke lambda's inner scope exited). The fix
    37+    // clears m_on_cancel in the cleanup loop->sync() so it is null by the
    38+    // time the scope exits.
    39+    //
    40+    // Use testing_hook_after_cleanup to trigger a server disconnect after the
    41+    // inner scope exits (cancel_mutex destroyed). Without the fix, the
    42+    // disconnect fires m_on_cancel which accesses the destroyed mutex.
    43+    TestSetup setup;
    44+    ProxyClient<messages::FooInterface>* foo = setup.client.get();
    45+    foo->initThreadMap();
    46+    setup.server->m_impl->m_fn = [] {};
    47+
    48+    setup.server->m_context.testing_hook_after_cleanup = [&] {
    49+        setup.server_disconnect();
    50+    };
    51+
    52+    try {
    53+        foo->callFnAsync();
    54+        KJ_EXPECT(false);
    55+    } catch (const std::runtime_error& e) {
    56+        KJ_EXPECT(std::string_view{e.what()} == "IPC client method call interrupted by disconnect.");
    57+    }
    58+#endif
    59+}
    60+
    61 KJ_TEST("Make simultaneous IPC calls on single remote thread")
    62 {
    63     TestSetup setup;
    64     ProxyClient<messages::FooInterface>* foo = setup.client.get();
    65     std::promise<void> signal;
    
  9. ryanofsky commented at 6:08 pm on March 10, 2026: contributor

    Here’s a test. It builds on the fix in #34777, the test I wrote in #34777 (comment) and your fix here.

    Very nice. It does seem it could make sense to open a libmultiprocess PR with the fixes:

    and your tests. I’m happy to do it, but it seems like you (and claude I assume) could be faster than me. I also think it would be good to deduplicate the tests a little but this could be a followup. Also it would seem ok to me to drop the NDEBUG checks but no real opinion about this. Just let me know what you prefer.

  10. ryanofsky referenced this in commit 7f954aa5ea on Mar 11, 2026
  11. ryanofsky referenced this in commit 9536b6334e on Mar 11, 2026
  12. ryanofsky commented at 3:52 am on March 11, 2026: contributor

    could make sense to open a libmultiprocess PR with the fixes

    This is done in https://github.com/bitcoin-core/libmultiprocess/pull/249

  13. Sjors referenced this in commit 06dc9edbca on Mar 11, 2026
  14. Sjors commented at 10:44 am on March 11, 2026: member

    but it seems like you (and claude I assume) could be faster than me

    Definitely had some help :-)

  15. Sjors referenced this in commit 789ac8c86d on Mar 11, 2026
  16. ryanofsky referenced this in commit 9b78b6043b on Mar 11, 2026
  17. ryanofsky referenced this in commit 473e09796e on Mar 12, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-03-13 15:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me