CConnman::Stop() resets semOutbound, yet m_reconnections is not cleared in Stop. Each ReconnectionInfo contains a grant member that points to the memory that semOutbound pointed to and ~CConnman will attempt to access the grant field (memory that was already freed) when destroying m_reconnections. Fix this by calling m_reconnections.clear() in CConnman::Stop() and add appropriate annotations.
I was able to reproduce the original issue #33615 with the following diff by randomly stopping my node while it was attempting to reconnect (and verified that this patch fixes the issue, at least in my ~40-50 runs):
0diff --git a/src/net.cpp b/src/net.cpp
1index ef1c63044a..9c1d161d8b 100644
2--- a/src/net.cpp
3+++ b/src/net.cpp
4@@ -1918,8 +1918,8 @@ void CConnman::DisconnectNodes()
5 {
6 LOCK(m_nodes_mutex);
7
8- const bool network_active{fNetworkActive};
9- if (!network_active) {
10+// const bool network_active{fNetworkActive};
11+// if (!network_active) {
12 // Disconnect any connected nodes
13 for (CNode* pnode : m_nodes) {
14 if (!pnode->fDisconnect) {
15@@ -1927,7 +1927,7 @@ void CConnman::DisconnectNodes()
16 pnode->fDisconnect = true;
17 }
18 }
19- }
20+// }
21
22 // Disconnect unused nodes
23 std::vector<CNode*> nodes_copy = m_nodes;
24@@ -1941,7 +1941,7 @@ void CConnman::DisconnectNodes()
25 // Add to reconnection list if appropriate. We don't reconnect right here, because
26 // the creation of a connection is a blocking operation (up to several seconds),
27 // and we don't want to hold up the socket handler thread for that long.
28- if (network_active && pnode->m_transport->ShouldReconnectV1()) {
29+ if (true) {
30 reconnections_to_add.push_back({
31 .addr_connect = pnode->addr,
32 .grant = std::move(pnode->grantOutbound),
I’m curious to see if others can reproduce as well.