Skip to content

Commit d9ba7ce

Browse files
authored
Merge pull request #1585 from TheBlueMatt/2022-06-copy_from_slice-sucks
Fix spurious panic on bogus funding txn that confirm and are spent
2 parents 8a97e58 + 6c480ae commit d9ba7ce

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)