Skip to content

Commit 24fd566

Browse files
authored
Merge pull request #18 from TheBlueMatt/2018-04-fuzzing-expansion
Expand full_stack_target from uselessly-large to laughably-large
2 parents f7dd692 + b21048a commit 24fd566

File tree

11 files changed

+266
-42
lines changed

11 files changed

+266
-42
lines changed

README.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,13 @@ Assorted random TODOs:
2727
a timer. By default we should provide an implementation of this which uses
2828
some newfangled rusty promise-y library, but should generally ensure a
2929
client can simply integrate this into whatever existing timer interface
30-
they use.
31-
32-
* Networking...having a simple bytes-in-bytes-out interface which does message
33-
handling and calls our encryption layer is probably the right approach. We
34-
should then also probably use the same promise-y library we use for timers
35-
to do socket selection and reading/writing.
30+
they use. (This is partially complete, but the events stuff needs to also
31+
exist in Channel, which has a few inline TODOs to set up timers).
3632

3733
* Figure out how to expose when-to-connect and who-to-connect-to.
3834

3935
* Implement when-to-connect and who-to-connect-to based on route/node rumoring
40-
and channelmanager state.
36+
and channelmanager state (and some concept of available value in wallet).
4137

4238
* Some kind of serialization format for on-disk storage of things like
4339
channels, channelmonitors, routing db, etc.
@@ -50,8 +46,6 @@ Assorted random TODOs:
5046

5147
* All the random TODOs and unimplemented!()s across the codebase.
5248

53-
* BOLT 11 (invoice/address creation/generation) implementation
54-
5549
* Type-ify our somewhat random usage of Uint256/[u8; 32]. Use Sha256dHash
5650
where appropriate, create our own types for everything else.
5751

fuzz/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ honggfuzz_fuzz = ["honggfuzz"]
1515
lightning = { path = "..", features = ["fuzztarget"] }
1616
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin", features = ["fuzztarget"] }
1717
secp256k1 = { version = "0.9", features = ["fuzztarget"] }
18+
rust-crypto = "0.2"
1819
honggfuzz = { version = "0.5", optional = true }
1920
afl = { version = "0.3", optional = true }
2021

fuzz/fuzz_targets/channel_target.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extern crate lightning;
33
extern crate secp256k1;
44

55
use bitcoin::blockdata::block::BlockHeader;
6-
use bitcoin::blockdata::transaction::Transaction;
6+
use bitcoin::blockdata::transaction::{Transaction, TxOut};
77
use bitcoin::util::hash::Sha256dHash;
88
use bitcoin::network::serialize::{serialize, BitcoinHash};
99

@@ -12,6 +12,7 @@ use lightning::ln::channelmanager::PendingForwardHTLCInfo;
1212
use lightning::ln::msgs;
1313
use lightning::ln::msgs::MsgDecodable;
1414
use lightning::chain::chaininterface::{FeeEstimator, ConfirmationTarget};
15+
use lightning::util::reset_rng_state;
1516

1617
use secp256k1::key::PublicKey;
1718
use secp256k1::Secp256k1;
@@ -89,6 +90,8 @@ impl<'a> FeeEstimator for FuzzEstimator<'a> {
8990

9091
#[inline]
9192
pub fn do_test(data: &[u8]) {
93+
reset_rng_state();
94+
9295
let input = InputData {
9396
data,
9497
read_pos: AtomicUsize::new(0),
@@ -163,18 +166,22 @@ pub fn do_test(data: &[u8]) {
163166

164167
let their_pubkey = get_pubkey!();
165168

166-
let tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
167-
let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);
169+
let mut tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
168170

169171
let mut channel = if get_slice!(1)[0] != 0 {
170-
let mut chan = Channel::new_outbound(&fee_est, their_pubkey, slice_to_be24(get_slice!(3)), get_slice!(1)[0] == 0, slice_to_be64(get_slice!(8)));
172+
let chan_value = slice_to_be24(get_slice!(3));
173+
let mut chan = Channel::new_outbound(&fee_est, their_pubkey, chan_value, get_slice!(1)[0] == 0, slice_to_be64(get_slice!(8)));
171174
chan.get_open_channel(Sha256dHash::from(get_slice!(32)), &fee_est).unwrap();
172175
let accept_chan = if get_slice!(1)[0] == 0 {
173176
decode_msg_with_len16!(msgs::AcceptChannel, 270, 1)
174177
} else {
175178
decode_msg!(msgs::AcceptChannel, 270)
176179
};
177180
return_err!(chan.accept_channel(&accept_chan));
181+
182+
tx.output.push(TxOut{ value: chan_value, script_pubkey: chan.get_funding_redeemscript().to_v0_p2wsh() });
183+
let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);
184+
178185
chan.get_outbound_funding_created(funding_output.0.clone(), funding_output.1).unwrap();
179186
let funding_signed = decode_msg!(msgs::FundingSigned, 32+64);
180187
return_err!(chan.funding_signed(&funding_signed));
@@ -190,6 +197,10 @@ pub fn do_test(data: &[u8]) {
190197
Err(_) => return,
191198
};
192199
chan.get_accept_channel().unwrap();
200+
201+
tx.output.push(TxOut{ value: open_chan.funding_satoshis, script_pubkey: chan.get_funding_redeemscript().to_v0_p2wsh() });
202+
let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);
203+
193204
let mut funding_created = decode_msg!(msgs::FundingCreated, 32+32+2+64);
194205
funding_created.funding_txid = funding_output.0.clone();
195206
funding_created.funding_output_index = funding_output.1;

fuzz/fuzz_targets/full_stack_target.rs

Lines changed: 156 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
extern crate bitcoin;
2+
extern crate crypto;
23
extern crate lightning;
34
extern crate secp256k1;
45

5-
use bitcoin::blockdata::transaction::Transaction;
6+
use bitcoin::blockdata::block::BlockHeader;
7+
use bitcoin::blockdata::transaction::{Transaction, TxOut};
8+
use bitcoin::blockdata::script::Script;
69
use bitcoin::network::constants::Network;
10+
use bitcoin::network::serialize::{serialize, BitcoinHash};
711
use bitcoin::util::hash::Sha256dHash;
12+
use bitcoin::util::uint::Uint256;
813

9-
use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,FeeEstimator,ChainWatchInterfaceUtil};
14+
use crypto::sha2::Sha256;
15+
use crypto::digest::Digest;
16+
17+
use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,ChainListener,FeeEstimator,ChainWatchInterfaceUtil};
1018
use lightning::ln::{channelmonitor,msgs};
1119
use lightning::ln::channelmanager::ChannelManager;
1220
use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor};
1321
use lightning::ln::router::Router;
22+
use lightning::util::events::{EventsProvider,Event};
23+
use lightning::util::reset_rng_state;
1424

1525
use secp256k1::key::{PublicKey,SecretKey};
1626
use secp256k1::Secp256k1;
1727

28+
use std::collections::HashMap;
1829
use std::sync::Arc;
1930
use std::sync::atomic::{AtomicUsize,Ordering};
2031

@@ -24,6 +35,13 @@ pub fn slice_to_be16(v: &[u8]) -> u16 {
2435
((v[1] as u16) << 8*0)
2536
}
2637

38+
#[inline]
39+
pub fn slice_to_be24(v: &[u8]) -> u32 {
40+
((v[0] as u32) << 8*2) |
41+
((v[1] as u32) << 8*1) |
42+
((v[2] as u32) << 8*0)
43+
}
44+
2745
#[inline]
2846
pub fn slice_to_be32(v: &[u8]) -> u32 {
2947
((v[0] as u32) << 8*3) |
@@ -32,6 +50,20 @@ pub fn slice_to_be32(v: &[u8]) -> u32 {
3250
((v[3] as u32) << 8*0)
3351
}
3452

53+
#[inline]
54+
pub fn be64_to_array(u: u64) -> [u8; 8] {
55+
let mut v = [0; 8];
56+
v[0] = ((u >> 8*7) & 0xff) as u8;
57+
v[1] = ((u >> 8*6) & 0xff) as u8;
58+
v[2] = ((u >> 8*5) & 0xff) as u8;
59+
v[3] = ((u >> 8*4) & 0xff) as u8;
60+
v[4] = ((u >> 8*3) & 0xff) as u8;
61+
v[5] = ((u >> 8*2) & 0xff) as u8;
62+
v[6] = ((u >> 8*1) & 0xff) as u8;
63+
v[7] = ((u >> 8*0) & 0xff) as u8;
64+
v
65+
}
66+
3567
struct InputData {
3668
data: Vec<u8>,
3769
read_pos: AtomicUsize,
@@ -44,13 +76,6 @@ impl InputData {
4476
}
4577
Some(&self.data[old_pos..old_pos + len])
4678
}
47-
fn get_slice_nonadvancing(&self, len: usize) -> Option<&[u8]> {
48-
let old_pos = self.read_pos.load(Ordering::Acquire);
49-
if self.data.len() < old_pos + len {
50-
return None;
51-
}
52-
Some(&self.data[old_pos..old_pos + len])
53-
}
5479
}
5580

5681
struct FuzzEstimator {
@@ -92,6 +117,8 @@ impl SocketDescriptor for Peer {
92117

93118
#[inline]
94119
pub fn do_test(data: &[u8]) {
120+
reset_rng_state();
121+
95122
let input = Arc::new(InputData {
96123
data: data.to_vec(),
97124
read_pos: AtomicUsize::new(0),
@@ -137,6 +164,12 @@ pub fn do_test(data: &[u8]) {
137164
}, our_network_key);
138165

139166
let mut peers = [false; 256];
167+
let mut should_forward = false;
168+
let mut payments_received = Vec::new();
169+
let mut payments_sent = 0;
170+
let mut pending_funding_generation: Vec<(Uint256, u64, Script)> = Vec::new();
171+
let mut pending_funding_signatures = HashMap::new();
172+
let mut pending_funding_relay = Vec::new();
140173

141174
loop {
142175
match get_slice!(1)[0] {
@@ -178,8 +211,122 @@ pub fn do_test(data: &[u8]) {
178211
Err(_) => { peers[peer_id as usize] = false; }
179212
}
180213
},
214+
4 => {
215+
let value = slice_to_be24(get_slice!(3)) as u64;
216+
let route = match router.get_route(&get_pubkey!(), &Vec::new(), value, 42) {
217+
Ok(route) => route,
218+
Err(_) => return,
219+
};
220+
let mut payment_hash = [0; 32];
221+
payment_hash[0..8].copy_from_slice(&be64_to_array(payments_sent));
222+
let mut sha = Sha256::new();
223+
sha.input(&payment_hash);
224+
sha.result(&mut payment_hash);
225+
for i in 1..32 { payment_hash[i] = 0; }
226+
payments_sent += 1;
227+
match channelmanager.send_payment(route, payment_hash) {
228+
Ok(_) => {},
229+
Err(_) => return,
230+
}
231+
},
232+
5 => {
233+
let peer_id = get_slice!(1)[0];
234+
if !peers[peer_id as usize] { return; }
235+
let their_key = get_pubkey!();
236+
let chan_value = slice_to_be24(get_slice!(3)) as u64;
237+
if channelmanager.create_channel(their_key, chan_value, 0).is_err() { return; }
238+
},
239+
6 => {
240+
let mut channels = channelmanager.list_channels();
241+
let channel_id = get_slice!(1)[0] as usize;
242+
if channel_id >= channels.len() { return; }
243+
channels.sort_by(|a, b| { a.channel_id.cmp(&b.channel_id) });
244+
if channelmanager.close_channel(&channels[channel_id].channel_id).is_err() { return; }
245+
},
246+
7 => {
247+
if should_forward {
248+
channelmanager.process_pending_htlc_forward();
249+
handler.process_events();
250+
should_forward = false;
251+
}
252+
},
253+
8 => {
254+
for payment in payments_received.drain(..) {
255+
let mut payment_preimage = None;
256+
for i in 0..payments_sent {
257+
let mut payment_hash = [0; 32];
258+
payment_hash[0..8].copy_from_slice(&be64_to_array(i));
259+
let mut sha = Sha256::new();
260+
sha.input(&payment_hash);
261+
sha.result(&mut payment_hash);
262+
for i in 1..32 { payment_hash[i] = 0; }
263+
if payment_hash == payment {
264+
payment_hash = [0; 32];
265+
payment_hash[0..8].copy_from_slice(&be64_to_array(i));
266+
payment_preimage = Some(payment_hash);
267+
break;
268+
}
269+
}
270+
channelmanager.claim_funds(payment_preimage.unwrap());
271+
}
272+
},
273+
9 => {
274+
for payment in payments_received.drain(..) {
275+
channelmanager.fail_htlc_backwards(&payment);
276+
}
277+
},
278+
10 => {
279+
for funding_generation in pending_funding_generation.drain(..) {
280+
let mut tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: vec![TxOut {
281+
value: funding_generation.1, script_pubkey: funding_generation.2,
282+
}] };
283+
let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);
284+
channelmanager.funding_transaction_generated(&funding_generation.0, funding_output.clone());
285+
pending_funding_signatures.insert(funding_output, tx);
286+
}
287+
},
288+
11 => {
289+
if !pending_funding_relay.is_empty() {
290+
let mut txn = Vec::with_capacity(pending_funding_relay.len());
291+
let mut txn_idxs = Vec::with_capacity(pending_funding_relay.len());
292+
for (idx, tx) in pending_funding_relay.iter().enumerate() {
293+
txn.push(tx);
294+
txn_idxs.push(idx as u32 + 1);
295+
}
296+
297+
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
298+
channelmanager.block_connected(&header, 1, &txn[..], &txn_idxs[..]);
299+
txn.clear();
300+
txn_idxs.clear();
301+
for i in 2..100 {
302+
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
303+
channelmanager.block_connected(&header, i, &txn[..], &txn_idxs[..]);
304+
}
305+
}
306+
pending_funding_relay.clear();
307+
},
181308
_ => return,
182309
}
310+
for event in handler.get_and_clear_pending_events() {
311+
match event {
312+
Event::FundingGenerationReady { temporary_channel_id, channel_value_satoshis, output_script, .. } => {
313+
pending_funding_generation.push((temporary_channel_id, channel_value_satoshis, output_script));
314+
},
315+
Event::FundingBroadcastSafe { funding_txo, .. } => {
316+
pending_funding_relay.push(pending_funding_signatures.remove(&funding_txo).unwrap());
317+
},
318+
Event::PaymentReceived { payment_hash, .. } => {
319+
payments_received.push(payment_hash);
320+
},
321+
Event::PaymentSent {..} => {},
322+
Event::PaymentFailed {..} => {},
323+
324+
Event::PendingHTLCsForwardable {..} => {
325+
should_forward = true;
326+
},
327+
_ => panic!("Unknown event"),
328+
}
329+
}
183330
}
184331
}
185332

fuzz/fuzz_targets/peer_crypt_target.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ extern crate lightning;
22
extern crate secp256k1;
33

44
use lightning::ln::peer_channel_encryptor::PeerChannelEncryptor;
5+
use lightning::util::reset_rng_state;
56

67
use secp256k1::key::{PublicKey,SecretKey};
78
use secp256k1::Secp256k1;
@@ -14,6 +15,8 @@ fn slice_to_be16(v: &[u8]) -> u16 {
1415

1516
#[inline]
1617
pub fn do_test(data: &[u8]) {
18+
reset_rng_state();
19+
1720
let mut read_pos = 0;
1821
macro_rules! get_slice {
1922
($len: expr) => {

src/ln/channel.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,10 +1639,16 @@ impl Channel {
16391639
if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 {
16401640
for (ref tx, index_in_block) in txn_matched.iter().zip(indexes_of_txn_matched) {
16411641
if tx.txid() == self.channel_monitor.get_funding_txo().unwrap().0 {
1642-
self.funding_tx_confirmations = 1;
1643-
self.short_channel_id = Some(((height as u64) << (5*8)) |
1644-
((*index_in_block as u64) << (2*8)) |
1645-
((self.channel_monitor.get_funding_txo().unwrap().1 as u64) << (2*8)));
1642+
let txo_idx = self.channel_monitor.get_funding_txo().unwrap().1 as usize;
1643+
if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() ||
1644+
tx.output[txo_idx].value != self.channel_value_satoshis {
1645+
self.channel_state = ChannelState::ShutdownComplete as u32;
1646+
} else {
1647+
self.funding_tx_confirmations = 1;
1648+
self.short_channel_id = Some(((height as u64) << (5*8)) |
1649+
((*index_in_block as u64) << (2*8)) |
1650+
((self.channel_monitor.get_funding_txo().unwrap().1 as u64) << (2*8)));
1651+
}
16461652
}
16471653
}
16481654
}

0 commit comments

Comments
 (0)