From 00bd849ee1c23468c471e615db2432ef5d24c4b8 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Mon, 25 May 2026 16:48:27 +0000 Subject: [PATCH] node: unregister old decrypt-worker entry on cross-connection-won promotion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cross-connection-won path in handle_msg1 removes the old peer and frees its allocated index, but does not unregister the old (transport_id, our_index) cache_key from the decrypt worker pool. The orphan entry sits in the per-shard HashMap until the index allocator recycles old_idx to a different peer and that peer's register_decrypt_worker_session call overwrites it. In the interim, any decrypt job that lands at the recycled cache_key resolves to the wrong session and AEAD silently fails — observed as multi-hop routing failure in 5-node static-mesh on next-branch where bidirectional auto_connect drives cross-connections at every peer pair on startup. --- src/node/handlers/handshake.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/node/handlers/handshake.rs b/src/node/handlers/handshake.rs index 742e2a4..2702e25 100644 --- a/src/node/handlers/handshake.rs +++ b/src/node/handlers/handshake.rs @@ -1009,6 +1009,17 @@ impl Node { (old_peer.transport_id(), old_peer.our_index()) { self.peers_by_index.remove(&(old_tid, old_idx.as_u32())); + // Unregister the OLD cache_key from the decrypt + // worker pool BEFORE freeing the index for reuse. + // Otherwise the worker's per-shard HashMap retains a + // stale entry pointing at the removed peer's session; + // if the index allocator later recycles old_idx to a + // different peer, the new register call overwrites + // the stale entry — but until that point, decrypt + // jobs that land at the recycled cache_key resolve + // to the wrong session and AEAD silently fails. + #[cfg(unix)] + self.unregister_decrypt_worker_session((old_tid, old_idx.as_u32())); let _ = self.index_allocator.free(old_idx); }