Skip to content

Commit 6c480ae

Browse files
committed
Fix spurious panic on bogus funding txn that confirm and are spent
In c02b6a3 we moved the `payment_preimage` copy from inside the macro which only runs if we are spending an output we know is an HTLC output to doing it for any script that matches our expected length. This can panic if an inbound channel is created with a bogus funding transaction that has a witness program of the HTLC-Success/-Offered length but which does not have a second-to-last witness element which is 32 bytes. Luckily this panic is relatively simple for downstream users to work around - if an invalid-length-copy panic occurs, simply remove the ChannelMonitor from the bogus channel on startup and run without it. Because the channel must be funded by a bogus script in order to reach this panic, the channel will already have closed by the time the funding transaction is spent, and there can be no local funds in such a channel, so removing the `ChannelMonitor` wholesale is completely safe. In order to test this we have to disable an in-line assertion that checks that our transactions match expected scripts which we do by checking for the specific bogus script that we now use in `test_invalid_funding_tx`. Thanks to Eugene Siegel for reporting this issue.
1 parent 8a97e58 commit 6c480ae

File tree

2 files changed

+78
-12
lines changed

2 files changed

+78
-12
lines changed

lightning/src/chain/channelmonitor.rs

+40-10
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,26 @@ macro_rules! fail_unbroadcast_htlcs {
17291729
} }
17301730
}
17311731

1732+
// In the `test_invalid_funding_tx` test, we need a bogus script which matches the HTLC-Accepted
1733+
// witness length match (ie is 136 bytes long). We generate one here which we also use in some
1734+
// in-line tests later.
1735+
1736+
#[cfg(test)]
1737+
pub fn deliberately_bogus_accepted_htlc_witness_program() -> Vec<u8> {
1738+
let mut ret = [opcodes::all::OP_NOP.into_u8(); 136];
1739+
ret[131] = opcodes::all::OP_DROP.into_u8();
1740+
ret[132] = opcodes::all::OP_DROP.into_u8();
1741+
ret[133] = opcodes::all::OP_DROP.into_u8();
1742+
ret[134] = opcodes::all::OP_DROP.into_u8();
1743+
ret[135] = opcodes::OP_TRUE.into_u8();
1744+
Vec::from(&ret[..])
1745+
}
1746+
1747+
#[cfg(test)]
1748+
pub fn deliberately_bogus_accepted_htlc_witness() -> Vec<Vec<u8>> {
1749+
vec![Vec::new(), Vec::new(), Vec::new(), Vec::new(), deliberately_bogus_accepted_htlc_witness_program().into()].into()
1750+
}
1751+
17321752
impl<Signer: Sign> ChannelMonitorImpl<Signer> {
17331753
/// Inserts a revocation secret into this channel monitor. Prunes old preimages if neither
17341754
/// needed by holder commitment transactions HTCLs nor by counterparty ones. Unless we haven't already seen
@@ -2701,14 +2721,21 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
27012721
if *idx == input.previous_output.vout {
27022722
#[cfg(test)]
27032723
{
2704-
// If the expected script is a known type, check that the witness
2705-
// appears to be spending the correct type (ie that the match would
2706-
// actually succeed in BIP 158/159-style filters).
2707-
if _script_pubkey.is_v0_p2wsh() {
2708-
assert_eq!(&bitcoin::Address::p2wsh(&Script::from(input.witness.last().unwrap().to_vec()), bitcoin::Network::Bitcoin).script_pubkey(), _script_pubkey);
2709-
} else if _script_pubkey.is_v0_p2wpkh() {
2710-
assert_eq!(&bitcoin::Address::p2wpkh(&bitcoin::PublicKey::from_slice(&input.witness.last().unwrap()).unwrap(), bitcoin::Network::Bitcoin).unwrap().script_pubkey(), _script_pubkey);
2711-
} else { panic!(); }
2724+
// If the expected script is a known type, check that the witness
2725+
// appears to be spending the correct type (ie that the match would
2726+
// actually succeed in BIP 158/159-style filters).
2727+
if _script_pubkey.is_v0_p2wsh() {
2728+
if input.witness.last().unwrap().to_vec() == deliberately_bogus_accepted_htlc_witness_program() {
2729+
// In at least one test we use a deliberately bogus witness
2730+
// script which hit an old panic. Thus, we check for that here
2731+
// and avoid the assert if its the expected bogus script.
2732+
return true;
2733+
}
2734+
2735+
assert_eq!(&bitcoin::Address::p2wsh(&Script::from(input.witness.last().unwrap().to_vec()), bitcoin::Network::Bitcoin).script_pubkey(), _script_pubkey);
2736+
} else if _script_pubkey.is_v0_p2wpkh() {
2737+
assert_eq!(&bitcoin::Address::p2wpkh(&bitcoin::PublicKey::from_slice(&input.witness.last().unwrap()).unwrap(), bitcoin::Network::Bitcoin).unwrap().script_pubkey(), _script_pubkey);
2738+
} else { panic!(); }
27122739
}
27132740
return true;
27142741
}
@@ -2793,10 +2820,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
27932820
let prev_last_witness_len = input.witness.second_to_last().map(|w| w.len()).unwrap_or(0);
27942821
let revocation_sig_claim = (witness_items == 3 && htlctype == Some(HTLCType::OfferedHTLC) && prev_last_witness_len == 33)
27952822
|| (witness_items == 3 && htlctype == Some(HTLCType::AcceptedHTLC) && prev_last_witness_len == 33);
2796-
let accepted_preimage_claim = witness_items == 5 && htlctype == Some(HTLCType::AcceptedHTLC);
2823+
let accepted_preimage_claim = witness_items == 5 && htlctype == Some(HTLCType::AcceptedHTLC)
2824+
&& input.witness.second_to_last().unwrap().len() == 32;
27972825
#[cfg(not(fuzzing))]
27982826
let accepted_timeout_claim = witness_items == 3 && htlctype == Some(HTLCType::AcceptedHTLC) && !revocation_sig_claim;
2799-
let offered_preimage_claim = witness_items == 3 && htlctype == Some(HTLCType::OfferedHTLC) && !revocation_sig_claim;
2827+
let offered_preimage_claim = witness_items == 3 && htlctype == Some(HTLCType::OfferedHTLC) &&
2828+
!revocation_sig_claim && input.witness.second_to_last().unwrap().len() == 32;
2829+
28002830
#[cfg(not(fuzzing))]
28012831
let offered_timeout_claim = witness_items == 5 && htlctype == Some(HTLCType::OfferedHTLC);
28022832

lightning/src/ln/functional_tests.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use util::config::UserConfig;
3737

3838
use bitcoin::hash_types::BlockHash;
3939
use bitcoin::blockdata::block::{Block, BlockHeader};
40-
use bitcoin::blockdata::script::Builder;
40+
use bitcoin::blockdata::script::{Builder, Script};
4141
use bitcoin::blockdata::opcodes;
4242
use bitcoin::blockdata::constants::genesis_block;
4343
use bitcoin::network::constants::Network;
@@ -9424,6 +9424,10 @@ fn test_invalid_funding_tx() {
94249424
// funding transactions from their counterparties, leading to a multi-implementation critical
94259425
// security vulnerability (though we always sanitized properly, we've previously had
94269426
// un-released crashes in the sanitization process).
9427+
//
9428+
// Further, if the funding transaction is consensus-valid, confirms, and is later spent, we'd
9429+
// previously have crashed in `ChannelMonitor` even though we closed the channel as bogus and
9430+
// gave up on it. We test this here by generating such a transaction.
94279431
let chanmon_cfgs = create_chanmon_cfgs(2);
94289432
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
94299433
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
@@ -9434,9 +9438,19 @@ fn test_invalid_funding_tx() {
94349438
nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()));
94359439

94369440
let (temporary_channel_id, mut tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100_000, 42);
9441+
9442+
// Create a witness program which can be spent by a 4-empty-stack-elements witness and which is
9443+
// 136 bytes long. This matches our "accepted HTLC preimage spend" matching, previously causing
9444+
// a panic as we'd try to extract a 32 byte preimage from a witness element without checking
9445+
// its length.
9446+
let mut wit_program: Vec<u8> = channelmonitor::deliberately_bogus_accepted_htlc_witness_program();
9447+
assert!(chan_utils::HTLCType::scriptlen_to_htlctype(wit_program.len()).unwrap() ==
9448+
chan_utils::HTLCType::AcceptedHTLC);
9449+
9450+
let wit_program_script: Script = wit_program.clone().into();
94379451
for output in tx.output.iter_mut() {
94389452
// Make the confirmed funding transaction have a bogus script_pubkey
9439-
output.script_pubkey = bitcoin::Script::new();
9453+
output.script_pubkey = Script::new_v0_p2wsh(&wit_program_script.wscript_hash());
94409454
}
94419455

94429456
nodes[0].node.funding_transaction_generated_unchecked(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone(), 0).unwrap();
@@ -9466,6 +9480,28 @@ fn test_invalid_funding_tx() {
94669480
} else { panic!(); }
94679481
} else { panic!(); }
94689482
assert_eq!(nodes[1].node.list_channels().len(), 0);
9483+
9484+
// Now confirm a spend of the (bogus) funding transaction. As long as the witness is 5 elements
9485+
// long the ChannelMonitor will try to read 32 bytes from the second-to-last element, panicing
9486+
// as its not 32 bytes long.
9487+
let mut spend_tx = Transaction {
9488+
version: 2i32, lock_time: 0,
9489+
input: tx.output.iter().enumerate().map(|(idx, _)| TxIn {
9490+
previous_output: BitcoinOutPoint {
9491+
txid: tx.txid(),
9492+
vout: idx as u32,
9493+
},
9494+
script_sig: Script::new(),
9495+
sequence: 0xfffffffd,
9496+
witness: Witness::from_vec(channelmonitor::deliberately_bogus_accepted_htlc_witness())
9497+
}).collect(),
9498+
output: vec![TxOut {
9499+
value: 1000,
9500+
script_pubkey: Script::new(),
9501+
}]
9502+
};
9503+
check_spends!(spend_tx, tx);
9504+
mine_transaction(&nodes[1], &spend_tx);
94699505
}
94709506

94719507
fn do_test_tx_confirmed_skipping_blocks_immediate_broadcast(test_height_before_timelock: bool) {

0 commit comments

Comments
 (0)