From 2d7f858a5498795b53218f422099bbd87c1ac078 Mon Sep 17 00:00:00 2001 From: JRG Date: Sat, 15 Aug 2026 15:18:34 +0300 Subject: [PATCH] Transport: fix deadlock on receipts_lock when callback sends message Deadlock on receipts_lock: inbound (Transport.py) <- holds receipts_lock validate_proof_packet (Packet.py) validate_link_proof (Packet.py) delivery_callback <- application callback, sends packet send (Packet.py) outbound (Transport.py) packet_sent (Transport.py) <- blocks on receipts_lock --- RNS/Transport.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/RNS/Transport.py b/RNS/Transport.py index e2f7d45b..60741c3a 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -2350,20 +2350,19 @@ class Transport: RNS.log("Proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None with Transport.receipts_lock: - for receipt in Transport.receipts: - receipt_validated = False - if proof_hash != None: - # Only test validation if hash matches - if receipt.hash == proof_hash: - receipt_validated = receipt.validate_proof_packet(packet) - else: - # In case of an implicit proof, we have - # to check every single outstanding receipt - receipt_validated = receipt.validate_proof_packet(packet) + if proof_hash != None: + # Only test validation if hash matches + candidate_receipts = [r for r in Transport.receipts if r.hash == proof_hash] + else: + # In case of an implicit proof, we have + # to check every single outstanding receipt + candidate_receipts = Transport.receipts.copy() - if receipt_validated: - if receipt in Transport.receipts: - Transport.receipts.remove(receipt) + for receipt in candidate_receipts: + if receipt.status != RNS.PacketReceipt.SENT: continue + if receipt.validate_proof_packet(packet): + with Transport.receipts_lock: + if receipt in Transport.receipts: Transport.receipts.remove(receipt) @staticmethod def synthesize_tunnel(interface):