|
| 1 | +# Force-close fuzzer LDK crashes |
| 2 | + |
| 3 | +Minimized crash sequences found by the chanmon_consistency fuzzer with |
| 4 | +force-close support. All crashes are `debug_assert` or `panic!` inside |
| 5 | +LDK, not in the fuzzer harness. Byte 0 encodes monitor styles (bits |
| 6 | +0-2) and channel type (bits 3-4: 0=Legacy, 1=KeyedAnchors). |
| 7 | + |
| 8 | +## 1. channelmonitor.rs:2727 - HTLC input not found in transaction |
| 9 | + |
| 10 | +``` |
| 11 | +debug_assert!(htlc_input_idx_opt.is_some()); |
| 12 | +``` |
| 13 | + |
| 14 | +When resolving an HTLC spend, the monitor searches for the HTLC |
| 15 | +outpoint in the spending transaction's inputs but doesn't find it. |
| 16 | +Falls back to index 0 in release mode, which would produce incorrect |
| 17 | +tracking. |
| 18 | + |
| 19 | +Minimized (17 bytes): |
| 20 | +``` |
| 21 | +0x40 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xdc 0xde 0xff |
| 22 | +``` |
| 23 | + |
| 24 | +Byte 0 = 0x40: Legacy channels, no async monitors. The sequence is |
| 25 | +mostly 0xff (settlement) repeated, with height advances (0xdc, 0xde) |
| 26 | +near the end. This suggests the crash happens during settlement when |
| 27 | +processing on-chain HTLC spends after repeated settlement attempts. |
| 28 | + |
| 29 | +## 2. onchaintx.rs:913 - Duplicate claim ID in pending requests |
| 30 | + |
| 31 | +``` |
| 32 | +debug_assert!(self.pending_claim_requests.get(&claim_id).is_none()); |
| 33 | +``` |
| 34 | + |
| 35 | +The OnchainTxHandler registers a claim event with a claim_id that |
| 36 | +already exists in the pending_claim_requests map. |
| 37 | + |
| 38 | +Minimized (10 bytes): |
| 39 | +``` |
| 40 | +0x08 0xd2 0x70 0x70 0x71 0x70 0x10 0x19 0xde 0xff |
| 41 | +``` |
| 42 | + |
| 43 | +Byte 0 = 0x08: KeyedAnchors channels, no async monitors. |
| 44 | +- 0xd2: B force-closes the A-B channel |
| 45 | +- 0x70/0x71: disconnect/reconnect peers |
| 46 | +- 0x10, 0x19: process messages on nodes A and B |
| 47 | +- 0xde: advance chain 200 blocks |
| 48 | +- 0xff: settle |
| 49 | + |
| 50 | +B force-closes, peers disconnect and reconnect, messages are exchanged, |
| 51 | +then height advances and settlement triggers the duplicate claim. |
| 52 | + |
| 53 | +## 3. onchaintx.rs:1025 - Inconsistent internal maps |
| 54 | + |
| 55 | +``` |
| 56 | +panic!("Inconsistencies between pending_claim_requests map and claimable_outpoints map"); |
| 57 | +``` |
| 58 | + |
| 59 | +The OnchainTxHandler detects that its `pending_claim_requests` and |
| 60 | +`claimable_outpoints` maps are out of sync. |
| 61 | + |
| 62 | +Minimized (14 bytes): |
| 63 | +``` |
| 64 | +0x00 0x3c 0x11 0x19 0xd0 0xde 0xff 0xff 0x19 0x21 0x19 0xde 0x26 0xff |
| 65 | +``` |
| 66 | + |
| 67 | +Byte 0 = 0x00: Legacy channels, all monitors completed. |
| 68 | +- 0x3c: send hop payment A->B->C (1M msat) |
| 69 | +- 0x11, 0x19: process messages to commit HTLC on A-B |
| 70 | +- 0xd0: A force-closes A-B |
| 71 | +- 0xde: advance 200 blocks |
| 72 | +- 0xff: settle (first round) |
| 73 | +- 0xff: settle again (second round, processes more messages) |
| 74 | +- 0x19, 0x21, 0x19: continue processing B and C messages |
| 75 | +- 0xde: advance 200 more blocks |
| 76 | +- 0x26: process events on node C |
| 77 | +- 0xff: settle (third round) |
| 78 | + |
| 79 | +A hop payment partially committed, then A force-closes. Multiple |
| 80 | +settlement rounds with continued message processing in between triggers |
| 81 | +the internal map inconsistency. |
| 82 | + |
| 83 | +## 4. test_channel_signer.rs:395 - Signing revoked commitment |
| 84 | + |
| 85 | +``` |
| 86 | +panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={}") |
| 87 | +``` |
| 88 | + |
| 89 | +The test channel signer is asked to sign an HTLC transaction for a |
| 90 | +commitment number that has already been revoked. |
| 91 | + |
| 92 | +Minimized (18 bytes): |
| 93 | +``` |
| 94 | +0x22 0x71 0x71 0x71 0x71 0x71 0x71 0x71 0xff 0xff 0xff 0xff 0xff 0xff 0xde 0xde 0xb5 0xff |
| 95 | +``` |
| 96 | + |
| 97 | +Byte 0 = 0x22: Legacy channels, async monitors on node B. |
| 98 | +- 0x71: disconnect B-C peers (repeated, only first effective) |
| 99 | +- 0xff: settle (repeated 6 times) |
| 100 | +- 0xde 0xde: advance 400 blocks |
| 101 | +- 0xb5: restart node B with alternate monitor state |
| 102 | +- 0xff: settle |
| 103 | + |
| 104 | +Async monitors on B with peer disconnection, repeated settlements, |
| 105 | +height advances, and a node restart with a different monitor state. |
| 106 | +The stale monitor combined with the restart puts B's signer in a state |
| 107 | +where it's asked to sign for an already-revoked commitment. |
| 108 | + |
| 109 | +## 5. channelmanager.rs:9836 - Payment blocker not found |
| 110 | + |
| 111 | +``` |
| 112 | +debug_assert!(found_blocker); |
| 113 | +``` |
| 114 | + |
| 115 | +During payment processing, the ChannelManager expects to find a |
| 116 | +specific blocker entry for an in-flight payment but it's missing. |
| 117 | + |
| 118 | +Minimized (13 bytes): |
| 119 | +``` |
| 120 | +0x00 0x3c 0x11 0x19 0x11 0x1f 0x19 0x21 0x19 0x27 0x27 0xde 0xff |
| 121 | +``` |
| 122 | + |
| 123 | +Byte 0 = 0x00: Legacy channels, all monitors completed. |
| 124 | +- 0x3c: send hop A->B->C (1M msat) |
| 125 | +- 0x11, 0x19, 0x11: commit HTLC on A-B |
| 126 | +- 0x1f: B processes events (forwards HTLC to C) |
| 127 | +- 0x19, 0x21, 0x19: commit HTLC on B-C |
| 128 | +- 0x27, 0x27: C processes events (claims payment) |
| 129 | +- 0xde: advance 200 blocks |
| 130 | +- 0xff: settle |
| 131 | + |
| 132 | +A straightforward A->B->C hop payment that completes normally (C |
| 133 | +claims), followed by a height advance and settlement. No force-close |
| 134 | +in this sequence, so the height advance before settlement may cause |
| 135 | +HTLC timeout processing that conflicts with the claim path. |
| 136 | + |
| 137 | +## 6. channelmanager.rs:19484 - Monitor update ID ordering violation |
| 138 | + |
| 139 | +``` |
| 140 | +debug_assert!(update.update_id >= pending_update.update_id); |
| 141 | +``` |
| 142 | + |
| 143 | +A ChannelMonitorUpdate has an update_id that is less than a pending |
| 144 | +update's id, violating the expected monotonic ordering. |
| 145 | + |
| 146 | +Minimized (10 bytes): |
| 147 | +``` |
| 148 | +0x84 0x70 0x11 0x19 0x11 0x1f 0xd0 0x11 0x1f 0xba |
| 149 | +``` |
| 150 | + |
| 151 | +Byte 0 = 0x84: Legacy channels, no async monitors, high bits set |
| 152 | +(bits 3-4 = 0, bits 7 and 2 set). |
| 153 | +- 0x70: disconnect A-B peers |
| 154 | +- 0x11, 0x19, 0x11: process messages (likely reestablish after setup) |
| 155 | +- 0x1f: process B events |
| 156 | +- 0xd0: A force-closes A-B channel |
| 157 | +- 0x11: process A messages |
| 158 | +- 0x1f: process B events |
| 159 | +- 0xba: restart node B with alternate monitor state |
| 160 | + |
| 161 | +A force-close followed by continued message/event processing and a |
| 162 | +node B restart triggers a monitor update with an out-of-order ID. |
0 commit comments