Merge pull request #63 from TheBlueMatt/master

Update fuzz target boilerplate to match rust-lightning
This commit is contained in:
Andrew Poelstra 2018-03-22 01:47:06 +00:00 committed by GitHub
commit 15fd85b9d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 356 additions and 31 deletions

View File

@ -5,9 +5,18 @@ rust:
- nightly - nightly
- 1.14.0 - 1.14.0
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y binutils-dev libunwind8-dev
install: install:
- git clone https://github.com/bitcoin/secp256k1.git - git clone https://github.com/bitcoin/secp256k1.git
- cd secp256k1 - cd secp256k1
- ./autogen.sh && ./configure && make && sudo make install - ./autogen.sh && ./configure && make && sudo make install
- sudo ldconfig /usr/local/lib - sudo ldconfig /usr/local/lib
- cd ..
script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi

View File

@ -17,6 +17,7 @@ path = "src/lib.rs"
[features] [features]
bitcoinconsenus = ["bitcoinconsensus"] bitcoinconsenus = ["bitcoinconsensus"]
fuzztarget = ["secp256k1/fuzztarget"]
[dependencies] [dependencies]
bitcoin-bech32 = "0.5.1" bitcoin-bech32 = "0.5.1"
@ -29,7 +30,5 @@ strason = "0.3"
bitcoinconsensus = { version = "0.16", optional=true } bitcoinconsensus = { version = "0.16", optional=true }
[dependencies.secp256k1] [dependencies.secp256k1]
version = "0.8" version = "0.9"
features = [ "rand", "serde" ] features = [ "rand" ]

View File

@ -1,4 +1,3 @@
[package] [package]
name = "bitcoin-fuzz" name = "bitcoin-fuzz"
version = "0.0.1" version = "0.0.1"
@ -8,15 +7,31 @@ publish = false
[package.metadata] [package.metadata]
cargo-fuzz = true cargo-fuzz = true
[dependencies.bitcoin] [features]
path = ".." afl_fuzz = ["afl"]
[dependencies.libfuzzer-sys] honggfuzz_fuzz = ["honggfuzz"]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
[dependencies]
honggfuzz = { version = "0.5", optional = true }
afl = { version = "0.3", optional = true }
bitcoin = { path = "..", features = ["fuzztarget"] }
# Prevent this from interfering with workspaces # Prevent this from interfering with workspaces
[workspace] [workspace]
members = ["."] members = ["."]
[[bin]] [[bin]]
name = "fuzzer_script_1" name = "deserialize_block"
path = "fuzzers/fuzzer_script_1.rs" path = "fuzz_targets/deserialize_block.rs"
[[bin]]
name = "deserialize_script"
path = "fuzz_targets/deserialize_script.rs"
[[bin]]
name = "deserialize_transaction"
path = "fuzz_targets/deserialize_transaction.rs"
[[bin]]
name = "deserialize_address"
path = "fuzz_targets/deserialize_address.rs"

View File

@ -0,0 +1,57 @@
extern crate bitcoin;
use std::str::FromStr;
fn do_test(data: &[u8]) {
let data_str = String::from_utf8_lossy(data);
let addr = match bitcoin::util::address::Address::from_str(&data_str) {
Ok(addr) => addr,
Err(_) => return,
};
assert_eq!(addr.to_string(), data_str);
}
#[cfg(feature = "afl")]
extern crate afl;
#[cfg(feature = "afl")]
fn main() {
afl::read_stdio_bytes(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}
#[cfg(test)]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;
for (idx, c) in hex.as_bytes().iter().enumerate() {
b <<= 4;
match *c {
b'A'...b'F' => b |= c - b'A' + 10,
b'a'...b'f' => b |= c - b'a' + 10,
b'0'...b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
out.push(b);
b = 0;
}
}
}
#[test]
fn duplicate_crash() {
let mut a = Vec::new();
extend_vec_from_hex("00000000", &mut a);
super::do_test(&a);
}
}

View File

@ -0,0 +1,52 @@
extern crate bitcoin;
type BResult = Result<bitcoin::blockdata::block::Block, bitcoin::util::Error>;
fn do_test(data: &[u8]) {
let _: BResult = bitcoin::network::serialize::deserialize(data);
}
#[cfg(feature = "afl")]
extern crate afl;
#[cfg(feature = "afl")]
fn main() {
afl::read_stdio_bytes(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}
#[cfg(test)]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;
for (idx, c) in hex.as_bytes().iter().enumerate() {
b <<= 4;
match *c {
b'A'...b'F' => b |= c - b'A' + 10,
b'a'...b'f' => b |= c - b'a' + 10,
b'0'...b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
out.push(b);
b = 0;
}
}
}
#[test]
fn duplicate_crash() {
let mut a = Vec::new();
extend_vec_from_hex("00", &mut a);
super::do_test(&a);
}
}

View File

@ -0,0 +1,52 @@
extern crate bitcoin;
type BResult = Result<bitcoin::blockdata::script::Script, bitcoin::util::Error>;
fn do_test(data: &[u8]) {
let _: BResult = bitcoin::network::serialize::deserialize(data);
}
#[cfg(feature = "afl")]
extern crate afl;
#[cfg(feature = "afl")]
fn main() {
afl::read_stdio_bytes(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}
#[cfg(test)]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;
for (idx, c) in hex.as_bytes().iter().enumerate() {
b <<= 4;
match *c {
b'A'...b'F' => b |= c - b'A' + 10,
b'a'...b'f' => b |= c - b'a' + 10,
b'0'...b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
out.push(b);
b = 0;
}
}
}
#[test]
fn duplicate_crash() {
let mut a = Vec::new();
extend_vec_from_hex("00", &mut a);
super::do_test(&a);
}
}

View File

@ -0,0 +1,52 @@
extern crate bitcoin;
type BResult = Result<bitcoin::blockdata::transaction::Transaction, bitcoin::util::Error>;
fn do_test(data: &[u8]) {
let _: BResult = bitcoin::network::serialize::deserialize(data);
}
#[cfg(feature = "afl")]
extern crate afl;
#[cfg(feature = "afl")]
fn main() {
afl::read_stdio_bytes(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}
#[cfg(test)]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;
for (idx, c) in hex.as_bytes().iter().enumerate() {
b <<= 4;
match *c {
b'A'...b'F' => b |= c - b'A' + 10,
b'a'...b'f' => b |= c - b'a' + 10,
b'0'...b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
out.push(b);
b = 0;
}
}
}
#[test]
fn duplicate_crash() {
let mut a = Vec::new();
extend_vec_from_hex("00", &mut a);
super::do_test(&a);
}
}

View File

@ -1,13 +0,0 @@
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate bitcoin;
type BResult = Result<bitcoin::blockdata::script::Script, bitcoin::util::Error>;
//type BResult = Result<bitcoin::blockdata::transaction::Transaction, bitcoin::util::Error>;
//type BResult = Result<bitcoin::blockdata::transaction::TxIn, bitcoin::util::Error>;
//type BResult = Result<bitcoin::blockdata::transaction::TxOut, bitcoin::util::Error>;
//type BResult = Result<bitcoin::network::constants::Network, bitcoin::util::Error>;
fuzz_target!(|data: &[u8]| {
let _: BResult = bitcoin::network::serialize::deserialize(data);
});

View File

@ -0,0 +1 @@
Ś¬ŹCCCACCCŢŚ¬ŞCCCAŹCCCACPC,¬3·¬ŹCCCC€

View File

@ -0,0 +1 @@
<EFBFBD>ウウウウウウウウウウウウヤウウウウウウウウウウウヤウウウウウウウウウウウウウウウウヤウウウ

15
fuzz/travis-fuzz.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
cargo install honggfuzz
set +e
for TARGET in fuzz_targets/*; do
FILENAME=$(basename $TARGET)
FILE="${FILENAME%.*}"
HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" HFUZZ_RUN_ARGS="-N1000000 --exit_upon_crash -v" cargo hfuzz run $FILE
if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then
cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT
for CASE in hfuzz_workspace/$FILE/SIG*; do
cat $CASE | xxd -p
done
exit 1
fi
done

View File

@ -28,7 +28,6 @@ use std::default::Default;
use std::{error, fmt}; use std::{error, fmt};
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha2::Sha256;
use serde; use serde;
use blockdata::opcodes; use blockdata::opcodes;
@ -39,6 +38,9 @@ use util::hash::Hash160;
#[cfg(feature="bitcoinconsensus")] use std::convert; #[cfg(feature="bitcoinconsensus")] use std::convert;
#[cfg(feature="bitcoinconsensus")] use util::hash::Sha256dHash; #[cfg(feature="bitcoinconsensus")] use util::hash::Sha256dHash;
#[cfg(feature="fuzztarget")] use util::sha2::Sha256;
#[cfg(not(feature="fuzztarget"))] use crypto::sha2::Sha256;
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
/// A Bitcoin script /// A Bitcoin script
pub struct Script(Box<[u8]>); pub struct Script(Box<[u8]>);

View File

@ -241,8 +241,9 @@ impl FromStr for Address {
fn from_str(s: &str) -> Result<Address, Error> { fn from_str(s: &str) -> Result<Address, Error> {
// bech32 (note that upper or lowercase is allowed but NOT mixed case) // bech32 (note that upper or lowercase is allowed but NOT mixed case)
if &s.as_bytes()[0..3] == b"bc1" || &s.as_bytes()[0..3] == b"tb1" || if s.len() >= 3 &&
&s.as_bytes()[0..3] == b"BC1" || &s.as_bytes()[0..3] == b"TB1" { (&s.as_bytes()[0..3] == b"bc1" || &s.as_bytes()[0..3] == b"tb1" ||
&s.as_bytes()[0..3] == b"BC1" || &s.as_bytes()[0..3] == b"TB1") {
let witprog = try!(WitnessProgram::from_address(s)); let witprog = try!(WitnessProgram::from_address(s));
let network = match witprog.network() { let network = match witprog.network() {
bitcoin_bech32::constants::Network::Bitcoin => Network::Bitcoin, bitcoin_bech32::constants::Network::Bitcoin => Network::Bitcoin,

View File

@ -28,14 +28,15 @@ use crypto::digest::Digest;
use crypto::hmac::Hmac; use crypto::hmac::Hmac;
use crypto::mac::Mac; use crypto::mac::Mac;
use crypto::ripemd160::Ripemd160; use crypto::ripemd160::Ripemd160;
use crypto::sha2::Sha256;
use crypto::sha2::Sha512;
use secp256k1::key::{PublicKey, SecretKey}; use secp256k1::key::{PublicKey, SecretKey};
use secp256k1::{self, Secp256k1}; use secp256k1::{self, Secp256k1};
use network::constants::Network; use network::constants::Network;
use util::base58; use util::base58;
#[cfg(feature="fuzztarget")] use util::sha2::{Sha256, Sha512};
#[cfg(not(feature="fuzztarget"))] use crypto::sha2::{Sha256, Sha512};
/// A chain code /// A chain code
pub struct ChainCode([u8; 32]); pub struct ChainCode([u8; 32]);
impl_array_newtype!(ChainCode, u8, 32); impl_array_newtype!(ChainCode, u8, 32);

View File

@ -20,7 +20,7 @@
use secp256k1::{self, Secp256k1}; use secp256k1::{self, Secp256k1};
use secp256k1::key::{PublicKey, SecretKey}; use secp256k1::key::{PublicKey, SecretKey};
use blockdata::{opcodes, script}; use blockdata::{opcodes, script};
use crypto::{hmac, sha2}; use crypto::hmac;
use crypto::mac::Mac; use crypto::mac::Mac;
use std::{error, fmt}; use std::{error, fmt};
@ -28,6 +28,9 @@ use std::{error, fmt};
use network::constants::Network; use network::constants::Network;
use util::{address, hash}; use util::{address, hash};
#[cfg(feature="fuzztarget")] use util::sha2;
#[cfg(not(feature="fuzztarget"))] use crypto::sha2;
/// Encoding of "pubkey here" in script; from bitcoin core `src/script/script.h` /// Encoding of "pubkey here" in script; from bitcoin core `src/script/script.h`
static PUBKEY: u8 = 0xFE; static PUBKEY: u8 = 0xFE;

View File

@ -26,13 +26,15 @@ use serde;
use byteorder::{LittleEndian, WriteBytesExt}; use byteorder::{LittleEndian, WriteBytesExt};
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha2::Sha256;
use crypto::ripemd160::Ripemd160; use crypto::ripemd160::Ripemd160;
use network::encodable::{ConsensusDecodable, ConsensusEncodable}; use network::encodable::{ConsensusDecodable, ConsensusEncodable};
use network::serialize::{SimpleEncoder, RawEncoder, BitcoinHash}; use network::serialize::{SimpleEncoder, RawEncoder, BitcoinHash};
use util::uint::Uint256; use util::uint::Uint256;
#[cfg(feature="fuzztarget")] use util::sha2::Sha256;
#[cfg(not(feature="fuzztarget"))] use crypto::sha2::Sha256;
/// Hex deserialization error /// Hex deserialization error
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum HexError { pub enum HexError {

View File

@ -28,6 +28,9 @@ pub mod iter;
pub mod misc; pub mod misc;
pub mod uint; pub mod uint;
#[cfg(feature = "fuzztarget")]
pub mod sha2;
use std::{error, fmt, io}; use std::{error, fmt, io};
use bitcoin_bech32; use bitcoin_bech32;

64
src/util/sha2.rs Normal file
View File

@ -0,0 +1,64 @@
//! fuzztarget-only Sha2 context with a dummy Sha256 and Sha512 hashers.
use crypto::digest::Digest;
use crypto::sha2;
#[derive(Clone, Copy)]
/// Dummy Sha256 that hashes the input, but only returns the first byte of output, masking the
/// rest to 0s.
pub struct Sha256 {
state: sha2::Sha256,
}
impl Sha256 {
/// Constructs a new dummy Sha256 context
pub fn new() -> Sha256 {
Sha256 {
state: sha2::Sha256::new(),
}
}
}
impl Digest for Sha256 {
fn result(&mut self, data: &mut [u8]) {
self.state.result(data);
for i in 1..32 {
data[i] = 0;
}
}
fn input(&mut self, data: &[u8]) { self.state.input(data); }
fn reset(&mut self) { self.state.reset(); }
fn output_bits(&self) -> usize { self.state.output_bits() }
fn block_size(&self) -> usize { self.state.block_size() }
}
#[derive(Clone, Copy)]
/// Dummy Sha512 that hashes the input, but only returns the first byte of output, masking the
/// rest to 0s.
pub struct Sha512 {
state: sha2::Sha512,
}
impl Sha512 {
/// Constructs a new dummy Sha512 context
pub fn new() -> Sha512 {
Sha512 {
state: sha2::Sha512::new(),
}
}
}
impl Digest for Sha512 {
fn result(&mut self, data: &mut [u8]) {
self.state.result(data);
for i in 1..64 {
data[i] = 0;
}
}
fn input(&mut self, data: &[u8]) { self.state.input(data); }
fn reset(&mut self) { self.state.reset(); }
fn output_bits(&self) -> usize { self.state.output_bits() }
fn block_size(&self) -> usize { self.state.block_size() }
}