Merge rust-bitcoin/rust-bitcoin#1732: Overhaul fuzzing

2860aae1a5 fuzz: don't fuzz hashes against RustCrypto (Andrew Poelstra)
6467728202 fuzz: disable tests unless 'cfg(fuzzing)' is passed; update README for reproducing failures (Andrew Poelstra)
6e2ee5be66 fuzz: run 'cargo fmt' on all the fuzz targets (Andrew Poelstra)
9cfc0fcd81 fuzz: add contrib/test.sh so we at least 'cargo test' it in CI (Andrew Poelstra)
933ecb19e1 fuzz: fix warnings, clippy lints, 1.48.0 failures (Andrew Poelstra)
fd88e48696 fuzz: remove AFL support (Andrew Poelstra)
ab467cb091 fuzz: make hongfuzz fuzzing the default feature (Andrew Poelstra)
6f754df231 fuzz: add fuzzing README (Andrew Poelstra)
f093765efe fix fuzz.sh and cycle.sh to use generated lists of targets (Andrew Poelstra)
6534f22362 fuzz: auto-generate CI and Cargo.toml files (Andrew Poelstra)
8021034d86 rename travis-fuzz.sh to fuzz.sh; partially patch CI (Andrew Poelstra)
0be75f7edc move hashes/fuzz into main fuzz/ directory (Andrew Poelstra)
5a891dec2d move bitcoin fuzz targets into bitcoin/ subdirectory (Andrew Poelstra)
e3111c748b move bitcoin/fuzz into repo root; add to workspace (Andrew Poelstra)

Pull request description:

  Several big changes here:
  * Moves fuzzing to its own workspace with a `contrib/test.sh` etc so that CI will check that it compiles
  * FIx all warnings, clippy lints, MSRV problems, etc.; mostly move to Rust 2018
  * Merge `hashes/` fuzztests into workspace
  * Rewrite all scripts; add file that auto-generates CI fuzz job and Cargo.toml so we don't have to manually keep these in sync
  * Remove bitrotted and partial AFL support.

  Supercedes #1422

  I suspect the hashes fuzztests will actually fail since we haven't touched them in so long. Will address that if CI fails here.

ACKs for top commit:
  sanket1729:
    ACK 2860aae1a5
  tcharding:
    ACK 2860aae1a5

Tree-SHA512: b1aa3d6fac75fee51966f1d3f3245784e331bdea2a3fa7d6609bc4196c34f81acb7701faf8f269c3741568ea100438f24a2f06e75c8d01cb84c8b22d7886f1dd
This commit is contained in:
Andrew Poelstra 2023-04-28 04:13:37 +00:00
commit c9347cd021
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
481 changed files with 813 additions and 755 deletions

View File

@ -1,3 +1,4 @@
# Automatically generated by fuzz/generate-files.sh
name: Fuzz
on:
@ -8,14 +9,32 @@ on:
pull_request:
jobs:
fuzz:
if: ${{ !github.event.act }}
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
fuzz_target: [deser_net_msg, deserialize_address, deserialize_amount, deserialize_block, deserialize_psbt, deserialize_script, deserialize_transaction, deserialize_prefilled_transaction, deserialize_witness, outpoint_string, script_bytes_to_asm_fmt]
fuzz_target: [
bitcoin_outpoint_string,
bitcoin_deserialize_amount,
bitcoin_deserialize_transaction,
bitcoin_deser_net_msg,
bitcoin_deserialize_address,
bitcoin_script_bytes_to_asm_fmt,
bitcoin_deserialize_prefilled_transaction,
bitcoin_deserialize_witness,
bitcoin_deserialize_psbt,
bitcoin_deserialize_block,
bitcoin_deserialize_script,
hashes_json,
hashes_cbor,
hashes_sha256,
hashes_ripemd160,
hashes_sha512_256,
hashes_sha512,
hashes_sha1,
]
steps:
- name: Install test dependencies
run: sudo apt-get update -y && sudo apt-get install -y binutils-dev libunwind8-dev libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc libiberty-dev
@ -34,8 +53,8 @@ jobs:
override: true
profile: minimal
- name: fuzz
run: cd bitcoin/fuzz && ./travis-fuzz.sh "${{ matrix.fuzz_target }}"
- run: echo "${{ matrix.fuzz_target }}.rs" >executed_${{ matrix.fuzz_target }}
run: cd fuzz && ./fuzz.sh "${{ matrix.fuzz_target }}"
- run: echo "${{ matrix.fuzz_target }}" >executed_${{ matrix.fuzz_target }}
- uses: actions/upload-artifact@v2
with:
name: executed_${{ matrix.fuzz_target }}
@ -51,5 +70,4 @@ jobs:
- name: Display structure of downloaded files
run: ls -R
- run: find executed_* -type f -exec cat {} + | sort > executed
- run: ls bitcoin/fuzz/fuzz_targets | sort > expected
- run: diff expected executed
- run: source ./fuzz/fuzz-util.sh && listTargetNames | sort | diff - executed

6
.gitignore vendored
View File

@ -16,7 +16,5 @@ hashes/target
bitcoin/dep_test
# Fuzz artifacts
bitcoin/fuzz/hfuzz_target
bitcoin/fuzz/hfuzz_workspace
hashes/fuzz/hfuzz_target
hashes/fuzz/hfuzz_workspace
hfuzz_target
hfuzz_workspace

View File

@ -1,5 +1,8 @@
[workspace]
members = ["bitcoin", "hashes", "internals"]
members = ["bitcoin", "hashes", "internals", "fuzz"]
[patch.crates-io.bitcoin]
path = "bitcoin"
[patch.crates-io.bitcoin_hashes]
path = "hashes"

View File

@ -82,6 +82,7 @@ To build with the MSRV you will need to pin `serde` (if you have the feature ena
```
# serde 1.0.157 uses syn 2.0 which requires edition 2021
cargo update -p serde --precise 1.0.156
cargo update -p half --precise 1.7.1
```
before building. (And if your code is a library, your downstream users will need to run these

View File

@ -108,16 +108,6 @@ if [ "$DO_DOCS" = true ]; then
RUSTDOCFLAGS="-D warnings" cargo +stable doc --all-features
fi
# Fuzz if told to
if [ "$DO_FUZZ" = true ]
then
(
cd fuzz
cargo test --verbose
./travis-fuzz.sh
)
fi
# Run formatter if told to.
if [ "$DO_FMT" = true ]; then
if [ "$NIGHTLY" = false ]; then

View File

@ -1,68 +0,0 @@
[package]
name = "bitcoin-fuzz"
version = "0.0.1"
authors = ["Automatically generated"]
publish = false
[package.metadata]
cargo-fuzz = true
[features]
afl_fuzz = ["afl"]
honggfuzz_fuzz = ["honggfuzz"]
[dependencies]
honggfuzz = { version = "0.5", optional = true, default-features = false }
afl = { version = "0.4", optional = true }
bitcoin = { path = "../" }
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "deserialize_block"
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_prefilled_transaction"
path = "fuzz_targets/deserialize_prefilled_transaction.rs"
[[bin]]
name = "deserialize_address"
path = "fuzz_targets/deserialize_address.rs"
[[bin]]
name = "deserialize_amount"
path = "fuzz_targets/deserialize_amount.rs"
[[bin]]
name = "outpoint_string"
path = "fuzz_targets/outpoint_string.rs"
[[bin]]
name = "deserialize_psbt"
path = "fuzz_targets/deserialize_psbt.rs"
[[bin]]
name = "deser_net_msg"
path = "fuzz_targets/deser_net_msg.rs"
[[bin]]
name = "script_bytes_to_asm_fmt"
path = "fuzz_targets/script_bytes_to_asm_fmt.rs"
[[bin]]
name = "deserialize_witness"
path = "fuzz_targets/deserialize_witness.rs"
[patch.crates-io.bitcoin_hashes]
path = "../../hashes"

View File

@ -1,23 +0,0 @@
#!/bin/bash
# Continuosly cycle over fuzz targets running each for 1 hour.
# It uses chrt SCHED_IDLE so that other process takes priority.
#
# For hfuzz options see https://github.com/google/honggfuzz/blob/master/docs/USAGE.md
export HFUZZ_BUILD_ARGS='--features honggfuzz_fuzz'
while :
do
for FILE in fuzz_targets/*;
do
TARGET=$(echo $FILE | cut -c 14- | cut -f 1 -d '.')
# fuzz for one hour
HFUZZ_RUN_ARGS='--run_time 3600' chrt -i 0 cargo hfuzz run $TARGET
# minimize the corpus
HFUZZ_RUN_ARGS="-i hfuzz_workspace/$TARGET/input/ -P -M" chrt -i 0 cargo hfuzz run $TARGET
done
done

View File

@ -1,41 +0,0 @@
extern crate bitcoin;
use std::fmt;
// faster than String, we don't need to actually produce the value, just check absence of panics
struct NullWriter;
impl fmt::Write for NullWriter {
fn write_str(&mut self, _s: &str) -> fmt::Result {
Ok(())
}
fn write_char(&mut self, _c: char) -> fmt::Result {
Ok(())
}
}
fn do_test(data: &[u8]) {
let mut writer = NullWriter;
bitcoin::Script::from_bytes(data).fmt_asm(&mut writer);
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}

View File

@ -1,38 +0,0 @@
#!/bin/bash
set -e
# Check that input files are correct Windows file names
incorrectFilenames=$(find . -type f -name "*,*" -o -name "*:*" -o -name "*<*" -o -name "*>*" -o -name "*|*" -o -name "*\?*" -o -name "*\**" -o -name "*\"*" | wc -l)
if [ ${incorrectFilenames} -gt 0 ]; then
exit 2
fi
if [ "$1" == "" ]; then
TARGETS=fuzz_targets/*
else
TARGETS=fuzz_targets/"$1".rs
fi
cargo --version
rustc --version
# Testing
cargo install --force honggfuzz --no-default-features
for TARGET in $TARGETS; do
echo "Fuzzing target $TARGET"
FILENAME=$(basename $TARGET)
FILE="${FILENAME%.*}"
if [ -d hfuzz_input/$FILE ]; then
HFUZZ_INPUT_ARGS="-f hfuzz_input/$FILE/input"
fi
HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" HFUZZ_RUN_ARGS="--run_time 30 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" 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

@ -2,7 +2,7 @@
set -ex
CRATES="bitcoin hashes internals"
CRATES="bitcoin hashes internals fuzz"
for crate in ${CRATES}
do

89
fuzz/Cargo.toml Normal file
View File

@ -0,0 +1,89 @@
[package]
name = "bitcoin-fuzz"
edition = "2018"
version = "0.0.1"
authors = ["Generated by fuzz/generate-files.sh"]
publish = false
[package.metadata]
cargo-fuzz = true
[dependencies]
honggfuzz = { version = "0.5", default-features = false }
bitcoin = { version = "0.30.0", features = [ "serde" ] }
serde = { version = "1.0.103", features = [ "derive" ] }
serde_json = "1.0"
serde_cbor = "0.9"
[[bin]]
name = "bitcoin_outpoint_string"
path = "fuzz_targets/bitcoin/outpoint_string.rs"
[[bin]]
name = "bitcoin_deserialize_amount"
path = "fuzz_targets/bitcoin/deserialize_amount.rs"
[[bin]]
name = "bitcoin_deserialize_transaction"
path = "fuzz_targets/bitcoin/deserialize_transaction.rs"
[[bin]]
name = "bitcoin_deser_net_msg"
path = "fuzz_targets/bitcoin/deser_net_msg.rs"
[[bin]]
name = "bitcoin_deserialize_address"
path = "fuzz_targets/bitcoin/deserialize_address.rs"
[[bin]]
name = "bitcoin_script_bytes_to_asm_fmt"
path = "fuzz_targets/bitcoin/script_bytes_to_asm_fmt.rs"
[[bin]]
name = "bitcoin_deserialize_prefilled_transaction"
path = "fuzz_targets/bitcoin/deserialize_prefilled_transaction.rs"
[[bin]]
name = "bitcoin_deserialize_witness"
path = "fuzz_targets/bitcoin/deserialize_witness.rs"
[[bin]]
name = "bitcoin_deserialize_psbt"
path = "fuzz_targets/bitcoin/deserialize_psbt.rs"
[[bin]]
name = "bitcoin_deserialize_block"
path = "fuzz_targets/bitcoin/deserialize_block.rs"
[[bin]]
name = "bitcoin_deserialize_script"
path = "fuzz_targets/bitcoin/deserialize_script.rs"
[[bin]]
name = "hashes_json"
path = "fuzz_targets/hashes/json.rs"
[[bin]]
name = "hashes_cbor"
path = "fuzz_targets/hashes/cbor.rs"
[[bin]]
name = "hashes_sha256"
path = "fuzz_targets/hashes/sha256.rs"
[[bin]]
name = "hashes_ripemd160"
path = "fuzz_targets/hashes/ripemd160.rs"
[[bin]]
name = "hashes_sha512_256"
path = "fuzz_targets/hashes/sha512_256.rs"
[[bin]]
name = "hashes_sha512"
path = "fuzz_targets/hashes/sha512.rs"
[[bin]]
name = "hashes_sha1"
path = "fuzz_targets/hashes/sha1.rs"

93
fuzz/README.md Normal file
View File

@ -0,0 +1,93 @@
# Fuzzing
`bitcoin` and `bitcoin_hashes` have fuzzing harnesses setup for use with
honggfuzz.
To run the fuzz-tests as in CI -- briefly fuzzing every target -- simply
run
./fuzz.sh
in this directory.
To build honggfuzz, you must have libunwind on your system, as well as
libopcodes and libbfd from binutils **2.38** on your system. The most
recently-released binutils 2.39 has changed their API in a breaking way.
On Nix, you can obtain these libraries by running
nix-shell -p libopcodes_2_38 -p libunwind
and then run fuzz.sh as above.
# Long-term fuzzing
To see the full list of targets, the most straightforward way is to run
source ./fuzz-util.sh
listTargetNames
To run each of them for an hour, run
./cycle.sh
To run a single fuzztest indefinitely, run
HFUZZ_BUILD_ARGS='--features honggfuzz_fuzz' cargo hfuzz run <target>
This script uses the `chrt` utility to try to reduce the priority of the
jobs. If you would like to run for longer, the most straightforward way
is to edit `cycle.sh` before starting. To run the fuzz-tests in parallel,
you will need to implement a custom harness.
# Adding fuzz tests
All fuzz tests can be found in the `fuzz_target/` directory. Adding a new
one is as simple as copying an existing one and editing the `do_test`
function to do what you want.
If your test clearly belongs to a specific crate, please put it in that
crate's directory. Otherwise you can put it directly in `fuzz_target/`.
If you need to add dependencies, edit the file `generate-files.sh` to add
it to the generated `Cargo.toml`.
Once you've added a fuzztest, regenerate the `Cargo.toml` and CI job by
running
./generate-files.sh
Then to test your fuzztest, run
./fuzz.sh <target>
If it is working, you will see a rapid stream of data for many seconds
(you can hit Ctrl+C to stop it early). If not, you should quickly see
an error.
# Reproducing Failures
If a fuzztest fails, it will exit with a summary which looks something like
```
...
fuzzTarget : hfuzz_target/x86_64-unknown-linux-gnu/release/hashes_sha256
CRASH:
DESCRIPTION:
ORIG_FNAME: 00000000000000000000000000000000.00000000.honggfuzz.cov
FUZZ_FNAME: hfuzz_workspace/hashes_sha256/SIGABRT.PC.7ffff7c8abc7.STACK.18826d9b64.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz
...
=====================================================================
fff400610004
```
The final line is a hex-encoded version of the input that caused the crash. You
can test this directly by editing the `duplicate_crash` test to copy/paste the
hex output into the call to `extend_vec_from_hex`. Then run the test with
RUSTFLAGS=--cfg=fuzzing cargo test
It is important to add the `cfg=fuzzing` flag, which tells rustc to compile the
library as though it were running a fuzztest. In particular, this will disable
or weaken all the cryptography.

44
fuzz/contrib/test.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/sh
set -ex
FEATURES=""
cargo --version
rustc --version
# Make all cargo invocations verbose
export CARGO_TERM_VERBOSE=true
# Pin dependencies as required if we are using MSRV toolchain.
if cargo --version | grep "1\.41"; then
# 1.0.157 uses syn 2.0 which requires edition 2021
cargo update -p serde --precise 1.0.156
# 1.0.108 uses `matches!` macro so does not work with Rust 1.41.1, bad `syn` no biscuit.
cargo update -p syn --precise 1.0.107
# Half 1.8 uses edition 2021 features
cargo update -p half --precise 1.7.1
fi
if [ "$DO_LINT" = true ]
then
cargo clippy --all-features --all-targets -- -D warnings
fi
# Defaults / sanity checks
cargo build
cargo test
# Address Sanitizer
if [ "$DO_ASAN" = true ]; then
cargo clean
CC='clang -fsanitize=address -fno-omit-frame-pointer' \
RUSTFLAGS='-Zsanitizer=address -Clinker=clang -Cforce-frame-pointers=yes' \
ASAN_OPTIONS='detect_leaks=1 detect_invalid_pointer_pairs=1 detect_stack_use_after_return=1' \
cargo test --lib --no-default-features --features="$FEATURES" -Zbuild-std --target x86_64-unknown-linux-gnu
cargo clean
CC='clang -fsanitize=memory -fno-omit-frame-pointer' \
RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins -Cforce-frame-pointers=yes' \
cargo test --lib --no-default-features --features="$FEATURES" -Zbuild-std --target x86_64-unknown-linux-gnu
fi

25
fuzz/cycle.sh Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Continuosly cycle over fuzz targets running each for 1 hour.
# It uses chrt SCHED_IDLE so that other process takes priority.
#
# For hfuzz options see https://github.com/google/honggfuzz/blob/master/docs/USAGE.md
set -e
REPO_DIR=$(git rev-parse --show-toplevel)
# shellcheck source=./fuzz-util.sh
source "$REPO_DIR/fuzz/fuzz-util.sh"
while :
do
for targetFile in $(listTargetFiles); do
targetName=$(targetFileToName "$targetFile")
echo "Fuzzing target $targetName ($targetFile)"
# fuzz for one hour
HFUZZ_RUN_ARGS='--run_time 3600' chrt -i 0 cargo hfuzz run "$targetName"
# minimize the corpus
HFUZZ_RUN_ARGS="-i hfuzz_workspace/$targetName/input/ -P -M" chrt -i 0 cargo hfuzz run "$targetName"
done
done

51
fuzz/fuzz-util.sh Executable file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env bash
REPO_DIR=$(git rev-parse --show-toplevel)
listTargetFiles() {
pushd "$REPO_DIR/fuzz" > /dev/null || exit 1
find fuzz_targets/ -type f -name "*.rs"
popd > /dev/null || exit 1
}
targetFileToName() {
echo "$1" \
| sed 's/^fuzz_targets\///' \
| sed 's/\.rs$//' \
| sed 's/\//_/g'
}
targetFileToHFuzzInputArg() {
baseName=$(basename "$1")
dirName="${baseName%.*}"
if [ -d "hfuzz_input/$dirName" ]; then
echo "HFUZZ_INPUT_ARGS=\"-f hfuzz_input/$FILE/input\""
fi
}
listTargetNames() {
for target in $(listTargetFiles); do
targetFileToName "$target"
done
}
# Utility function to avoid CI failures on Windows
checkWindowsFiles() {
incorrectFilenames=$(find . -type f -name "*,*" -o -name "*:*" -o -name "*<*" -o -name "*>*" -o -name "*|*" -o -name "*\?*" -o -name "*\**" -o -name "*\"*" | wc -l)
if [ "$incorrectFilenames" -gt 0 ]; then
echo "Bailing early because there is a Windows-incompatible filename in the tree."
exit 2
fi
}
# Checks whether a fuzz case output some report, and dumps it in hex
checkReport() {
reportFile="hfuzz_workspace/$1/HONGGFUZZ.REPORT.TXT"
if [ -f "$reportFile" ]; then
cat "$reportFile"
for CASE in "hfuzz_workspace/$1/SIG"*; do
xxd -p -c10000 < "$CASE"
done
exit 1
fi
}

34
fuzz/fuzz.sh Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -ex
REPO_DIR=$(git rev-parse --show-toplevel)
# shellcheck source=./fuzz-util.sh
source "$REPO_DIR/fuzz/fuzz-util.sh"
# Check that input files are correct Windows file names
checkWindowsFiles
if [ "$1" == "" ]; then
targetFiles="$(listTargetFiles)"
else
targetFiles=fuzz_targets/"$1".rs
fi
cargo --version
rustc --version
# Testing
cargo install --force honggfuzz --no-default-features
for targetFile in $targetFiles; do
targetName=$(targetFileToName "$targetFile")
echo "Fuzzing target $targetName ($targetFile)"
if [ -d "hfuzz_input/$targetName" ]; then
HFUZZ_INPUT_ARGS="-f hfuzz_input/$targetName/input\""
else
HFUZZ_INPUT_ARGS=""
fi
HFUZZ_RUN_ARGS="--run_time 30 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run "$targetName"
checkReport "$targetName"
done

View File

@ -1,21 +1,10 @@
extern crate bitcoin;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let _: Result<bitcoin::network::message::RawNetworkMessage, _> = bitcoin::consensus::encode::deserialize(data);
let _: Result<bitcoin::network::message::RawNetworkMessage, _> =
bitcoin::consensus::encode::deserialize(data);
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -24,7 +13,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,5 +1,7 @@
extern crate bitcoin;
use std::str::FromStr;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let data_str = String::from_utf8_lossy(data);
let addr = match bitcoin::address::Address::from_str(&data_str) {
@ -9,18 +11,6 @@ fn do_test(data: &[u8]) {
assert_eq!(addr.to_string(), data_str);
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -29,7 +19,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,5 +1,7 @@
extern crate bitcoin;
use std::str::FromStr;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let data_str = String::from_utf8_lossy(data);
@ -26,18 +28,6 @@ fn do_test(data: &[u8]) {
assert_eq!(amt, amt_roundtrip);
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -46,7 +36,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,21 +1,10 @@
extern crate bitcoin;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let _: Result<bitcoin::blockdata::block::Block, _>= bitcoin::consensus::encode::deserialize(data);
let _: Result<bitcoin::blockdata::block::Block, _> =
bitcoin::consensus::encode::deserialize(data);
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -24,7 +13,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,30 +1,19 @@
extern crate bitcoin;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
// We already fuzz Transactions in `./deserialize_transaction.rs`.
let tx_result: Result<bitcoin::bip152::PrefilledTransaction, _> = bitcoin::consensus::encode::deserialize(data);
let tx_result: Result<bitcoin::bip152::PrefilledTransaction, _> =
bitcoin::consensus::encode::deserialize(data);
match tx_result {
Err(_) => {},
Ok(mut tx) => {
Err(_) => {}
Ok(tx) => {
let ser = bitcoin::consensus::encode::serialize(&tx);
assert_eq!(&ser[..], data);
}
}
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -33,7 +22,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,9 +1,10 @@
extern crate bitcoin;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let psbt: Result<bitcoin::psbt::PartiallySignedTransaction, _> = bitcoin::psbt::Psbt::deserialize(data);
let psbt: Result<bitcoin::psbt::PartiallySignedTransaction, _> =
bitcoin::psbt::Psbt::deserialize(data);
match psbt {
Err(_) => {},
Err(_) => {}
Ok(psbt) => {
let ser = bitcoin::psbt::Psbt::serialize(&psbt);
let deser = bitcoin::psbt::Psbt::deserialize(&ser).unwrap();
@ -13,18 +14,6 @@ fn do_test(data: &[u8]) {
}
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -33,7 +22,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,9 +1,8 @@
extern crate bitcoin;
use bitcoin::address::Address;
use bitcoin::network::constants::Network;
use bitcoin::blockdata::script;
use bitcoin::consensus::encode;
use bitcoin::network::constants::Network;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let s: Result<script::ScriptBuf, _> = encode::deserialize(data);
@ -16,7 +15,9 @@ fn do_test(data: &[u8]) {
return;
}
match ins.ok().unwrap() {
script::Instruction::Op(op) => { b = b.push_opcode(op); }
script::Instruction::Op(op) => {
b = b.push_opcode(op);
}
script::Instruction::PushBytes(bytes) => {
// Any one-byte pushes, except -0, which can be interpreted as numbers, should be
// reserialized as numbers. (For -1 through 16, this will use special ops; for
@ -43,18 +44,6 @@ fn do_test(data: &[u8]) {
}
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -63,7 +52,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,9 +1,10 @@
extern crate bitcoin;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let tx_result: Result<bitcoin::blockdata::transaction::Transaction, _> = bitcoin::consensus::encode::deserialize(data);
let tx_result: Result<bitcoin::blockdata::transaction::Transaction, _> =
bitcoin::consensus::encode::deserialize(data);
match tx_result {
Err(_) => {},
Err(_) => {}
Ok(mut tx) => {
let ser = bitcoin::consensus::encode::serialize(&tx);
assert_eq!(&ser[..], data);
@ -21,22 +22,10 @@ fn do_test(data: &[u8]) {
} else {
assert_eq!(no_witness_len * 3 + len, calculated_weight);
}
},
}
}
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -45,7 +34,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,28 +1,15 @@
extern crate bitcoin;
use bitcoin::consensus::{serialize, deserialize};
use bitcoin::blockdata::witness::Witness;
use bitcoin::consensus::{deserialize, serialize};
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let w: Result<Witness, _> = deserialize(data);
if let Ok(witness) = w {
let serialized = serialize(&witness);
assert_eq!(data, serialized);
assert_eq!(data, &serialized[..]);
}
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -31,7 +18,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -1,21 +1,22 @@
extern crate bitcoin;
use std::str::FromStr;
use bitcoin::blockdata::transaction::OutPoint;
use bitcoin::consensus::encode;
use std::str::FromStr;
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let lowercase: Vec<u8> = data.iter().map(|c| match *c {
let lowercase: Vec<u8> = data
.iter()
.map(|c| match *c {
b'A' => b'a',
b'B' => b'b',
b'C' => b'c',
b'D' => b'd',
b'E' => b'e',
b'F' => b'f',
x => x
}).collect();
x => x,
})
.collect();
let data_str = match String::from_utf8(lowercase) {
Err(_) => return,
Ok(s) => s,
@ -33,25 +34,13 @@ fn do_test(data: &[u8]) {
let string = deser.to_string();
match OutPoint::from_str(&string) {
Ok(destring) => assert_eq!(destring, deser),
Err(_) => panic!()
Err(_) => panic!(),
}
}
}
}
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
@ -60,7 +49,7 @@ fn main() {
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;

View File

@ -0,0 +1,52 @@
use std::fmt;
use honggfuzz::fuzz;
// faster than String, we don't need to actually produce the value, just check absence of panics
struct NullWriter;
impl fmt::Write for NullWriter {
fn write_str(&mut self, _s: &str) -> fmt::Result { Ok(()) }
fn write_char(&mut self, _c: char) -> fmt::Result { Ok(()) }
}
fn do_test(data: &[u8]) {
let mut writer = NullWriter;
bitcoin::Script::from_bytes(data).fmt_asm(&mut writer).unwrap();
}
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}
#[cfg(all(test, fuzzing))]
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("00000", &mut a);
super::do_test(&a);
}
}

View File

@ -1,11 +1,6 @@
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate bitcoin_hashes;
extern crate serde_cbor;
use bitcoin_hashes::Hmac;
use bitcoin_hashes::{sha1, sha512, ripemd160, sha256d};
use bitcoin::hashes::{ripemd160, sha1, sha256d, sha512, Hmac};
use honggfuzz::fuzz;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct Hmacs {
@ -27,27 +22,22 @@ fn do_test(data: &[u8]) {
}
}
#[cfg(feature = "honggfuzz")]
#[macro_use]
extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|d| { do_test(d) });
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
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',
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 {

View File

@ -1,11 +1,6 @@
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate bitcoin_hashes;
extern crate serde_json;
use bitcoin_hashes::Hmac;
use bitcoin_hashes::{sha1, sha512, ripemd160, sha256d};
use bitcoin::hashes::{ripemd160, sha1, sha256d, sha512, Hmac};
use honggfuzz::fuzz;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct Hmacs {
@ -27,27 +22,22 @@ fn do_test(data: &[u8]) {
}
}
#[cfg(feature = "honggfuzz")]
#[macro_use]
extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|d| { do_test(d) });
}
}
#[cfg(test)]
#[cfg(all(test, fuzzing))]
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',
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 {

View File

@ -0,0 +1,44 @@
use bitcoin::hashes::{ripemd160, Hash, HashEngine};
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let mut engine = ripemd160::Hash::engine();
engine.input(data);
let eng_hash = ripemd160::Hash::from_engine(engine);
let hash = ripemd160::Hash::hash(data);
assert_eq!(&hash[..], &eng_hash[..]);
}
fn main() {
loop {
fuzz!(|d| { do_test(d) });
}
}
#[cfg(all(test, fuzzing))]
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("00000", &mut a);
super::do_test(&a);
}
}

View File

@ -0,0 +1,44 @@
use bitcoin::hashes::{sha1, Hash, HashEngine};
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let mut engine = sha1::Hash::engine();
engine.input(data);
let eng_hash = sha1::Hash::from_engine(engine);
let hash = sha1::Hash::hash(data);
assert_eq!(&hash[..], &eng_hash[..]);
}
fn main() {
loop {
fuzz!(|d| { do_test(d) });
}
}
#[cfg(all(test, fuzzing))]
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("00000", &mut a);
super::do_test(&a);
}
}

View File

@ -0,0 +1,44 @@
use bitcoin::hashes::{sha256, Hash, HashEngine};
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let mut engine = sha256::Hash::engine();
engine.input(data);
let eng_hash = sha256::Hash::from_engine(engine);
let hash = sha256::Hash::hash(data);
assert_eq!(&hash[..], &eng_hash[..]);
}
fn main() {
loop {
fuzz!(|d| { do_test(d) });
}
}
#[cfg(all(test, fuzzing))]
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("fff400610004", &mut a);
super::do_test(&a);
}
}

View File

@ -0,0 +1,44 @@
use bitcoin::hashes::{sha512, Hash, HashEngine};
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let mut engine = sha512::Hash::engine();
engine.input(data);
let eng_hash = sha512::Hash::from_engine(engine);
let hash = sha512::Hash::hash(data);
assert_eq!(&hash[..], &eng_hash[..]);
}
fn main() {
loop {
fuzz!(|d| { do_test(d) });
}
}
#[cfg(all(test, fuzzing))]
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("00000", &mut a);
super::do_test(&a);
}
}

View File

@ -0,0 +1,44 @@
use bitcoin::hashes::{sha512_256, Hash, HashEngine};
use honggfuzz::fuzz;
fn do_test(data: &[u8]) {
let mut engine = sha512_256::Hash::engine();
engine.input(data);
let eng_hash = sha512_256::Hash::from_engine(engine);
let hash = sha512_256::Hash::hash(data);
assert_eq!(&hash[..], &eng_hash[..]);
}
fn main() {
loop {
fuzz!(|d| { do_test(d) });
}
}
#[cfg(all(test, fuzzing))]
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("00000", &mut a);
super::do_test(&a);
}
}

100
fuzz/generate-files.sh Executable file
View File

@ -0,0 +1,100 @@
#!/usr/bin/env bash
set -e
REPO_DIR=$(git rev-parse --show-toplevel)
# shellcheck source=./fuzz-util.sh
source "$REPO_DIR/fuzz/fuzz-util.sh"
# 1. Generate fuzz/Cargo.toml
cat > "$REPO_DIR/fuzz/Cargo.toml" <<EOF
[package]
name = "bitcoin-fuzz"
edition = "2018"
version = "0.0.1"
authors = ["Generated by fuzz/generate-files.sh"]
publish = false
[package.metadata]
cargo-fuzz = true
[dependencies]
honggfuzz = { version = "0.5", default-features = false }
bitcoin = { version = "0.30.0", features = [ "serde" ] }
serde = { version = "1.0.103", features = [ "derive" ] }
serde_json = "1.0"
serde_cbor = "0.9"
EOF
for targetFile in $(listTargetFiles); do
targetName=$(targetFileToName "$targetFile")
cat >> "$REPO_DIR/fuzz/Cargo.toml" <<EOF
[[bin]]
name = "$targetName"
path = "$targetFile"
EOF
done
# 2. Generate .github/workflows/fuzz.yml
cat > "$REPO_DIR/.github/workflows/fuzz.yml" <<EOF
# Automatically generated by fuzz/generate-files.sh
name: Fuzz
on:
push:
branches:
- master
- 'test-ci/**'
pull_request:
jobs:
fuzz:
if: \${{ !github.event.act }}
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
fuzz_target: [
$(for name in $(listTargetNames); do echo "$name,"; done)
]
steps:
- name: Install test dependencies
run: sudo apt-get update -y && sudo apt-get install -y binutils-dev libunwind8-dev libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc libiberty-dev
- uses: actions/checkout@v2
- uses: actions/cache@v2
id: cache-fuzz
with:
path: |
~/.cargo/bin
fuzz/target
target
key: cache-\${{ matrix.target }}-\${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.58
override: true
profile: minimal
- name: fuzz
run: cd fuzz && ./fuzz.sh "\${{ matrix.fuzz_target }}"
- run: echo "\${{ matrix.fuzz_target }}" >executed_\${{ matrix.fuzz_target }}
- uses: actions/upload-artifact@v2
with:
name: executed_\${{ matrix.fuzz_target }}
path: executed_\${{ matrix.fuzz_target }}
verify-execution:
if: \${{ !github.event.act }}
needs: fuzz
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
- name: Display structure of downloaded files
run: ls -R
- run: find executed_* -type f -exec cat {} + | sort > executed
- run: source ./fuzz/fuzz-util.sh && listTargetNames | sort | diff - executed
EOF

Some files were not shown because too many files have changed in this diff Show More