Skip to content

Commit 9d11042

Browse files
committed
bugfix: prevent double delete segfault in server if client disconnects during method call
Fix issue where if a client disconnects in the middle of a long running IPC method call, the server will segfault when the method eventually returns. This behavior was reported by tdb3 in bitcoin/bitcoin#31003 (review) including a stack trace showing where the crash happens in the PassField mp.Context overload while calling request_threads.erase.
1 parent 181837b commit 9d11042

File tree

2 files changed

+17
-3
lines changed

2 files changed

+17
-3
lines changed

include/mp/proxy-types.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,19 @@ auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn&
152152
// inserted, one already existed, meaning this must be a
153153
// recursive call (IPC call calling back to the caller which
154154
// makes another IPC call), so avoid modifying the map.
155-
auto erase_thread{inserted ? request_thread : request_threads.end()};
156-
KJ_DEFER(if (erase_thread != request_threads.end()) {
155+
const bool erase_thread{inserted};
156+
KJ_DEFER(if (erase_thread) {
157157
std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
158-
request_threads.erase(erase_thread);
158+
// Call erase here with a Connection* argument instead
159+
// of an iterator argument, because the `request_thread`
160+
// iterator may be invalid if the connection is closed
161+
// during this function call. More specifically, the
162+
// iterator may be invalid because SetThread adds a
163+
// cleanup callback to the Connection destructor that
164+
// erases the thread from the map, and also because the
165+
// ProxyServer<Thread> destructor calls
166+
// request_threads.clear().
167+
request_threads.erase(server.m_context.connection);
159168
});
160169
fn.invoke(server_context, args...);
161170
}

src/mp/proxy.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ std::tuple<ConnThread, bool> SetThread(ConnThreads& threads, std::mutex& mutex,
277277
std::piecewise_construct, std::forward_as_tuple(connection),
278278
std::forward_as_tuple(make_thread(), connection, /* destroy_connection= */ false)).first;
279279
thread->second.setCleanup([&threads, &mutex, thread] {
280+
// Note: it is safe to use the `thread` iterator in this cleanup
281+
// function, because the iterator would only be invalid if the map entry
282+
// was removed, and if the map entry is removed the ProxyClient<Thread>
283+
// destructor unregisters the cleanup.
284+
280285
// Connection is being destroyed before thread client is, so reset
281286
// thread client m_cleanup member so thread client destructor does not
282287
// try unregister this callback after connection is destroyed.

0 commit comments

Comments
 (0)