Skip to content

Commit c9c7e7e

Browse files
committed
convert all fuzztests from afl+honggfuzz to libfuzzer
honggfuzz is faster but has always been unreliable and frustrating to keep working; I eventually removed it entirely from my local CI and we removed it from the Github CI here in rust-elements. AFL, meanwhile, I think we literally never used, though we copied it from Matt's old rust-lightning stuff. Anyway, something that works is better than something that's fast, so move to libfuzzer, which I can run in both my local CI and in Github.
1 parent d8061b8 commit c9c7e7e

9 files changed

Lines changed: 308 additions & 114 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Automatically generated by fuzz/generate-files.sh
2+
name: Fuzz
3+
on:
4+
schedule:
5+
# 5am every day UTC, this correlates to:
6+
# - 10pm PDT
7+
# - 6am CET
8+
# - 4pm AEDT
9+
- cron: '00 05 * * *'
10+
permissions: {}
11+
12+
jobs:
13+
fuzz:
14+
if: ${{ !github.event.act }}
15+
runs-on: ubuntu-24.04
16+
permissions:
17+
contents: read
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
fuzz_target: [
22+
deserialize_block,
23+
deserialize_output,
24+
deserialize_pset,
25+
deserialize_transaction,
26+
]
27+
steps:
28+
- name: Install test dependencies
29+
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
30+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
31+
with:
32+
persist-credentials: false
33+
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
34+
id: cache-fuzz
35+
with:
36+
path: |
37+
~/.cargo/bin
38+
fuzz/target
39+
target
40+
key: cache-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
41+
- uses: dtolnay/rust-toolchain@5d458579430fc14a04a08a1e7d3694f545e91ce6 # stable
42+
with:
43+
toolchain: '1.74.0'
44+
- name: fuzz
45+
run: |
46+
echo "Using RUSTFLAGS $RUSTFLAGS"
47+
cd fuzz && ./fuzz.sh "${{ matrix.fuzz_target }}"
48+
- run: echo "${{ matrix.fuzz_target }}" >executed_${{ matrix.fuzz_target }}
49+
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
50+
with:
51+
name: executed_${{ matrix.fuzz_target }}
52+
path: executed_${{ matrix.fuzz_target }}
53+
54+
verify-execution:
55+
if: ${{ !github.event.act }}
56+
needs: fuzz
57+
runs-on: ubuntu-24.04
58+
permissions:
59+
contents: read
60+
steps:
61+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
62+
with:
63+
persist-credentials: false
64+
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
65+
- run: cargo install --locked --version 0.12.0 cargo-fuzz
66+
- name: Display structure of downloaded files
67+
run: ls -R
68+
- run: find executed_* -type f -exec cat {} + | sort > executed
69+
- run: cargo fuzz list | sort | diff - executed

fuzz/Cargo.toml

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,56 @@
11
[package]
22
name = "elements-fuzz"
3+
edition = "2021"
4+
rust-version = "1.74.0"
35
version = "0.0.1"
4-
authors = ["Automatically generated"]
6+
authors = ["Generated by fuzz/generate-files.sh"]
57
publish = false
6-
edition = "2018"
78

89
[package.metadata]
910
cargo-fuzz = true
1011

11-
[features]
12-
afl_fuzz = ["afl"]
13-
honggfuzz_fuzz = ["honggfuzz"]
14-
1512
[dependencies]
16-
honggfuzz = { version = "0.5", optional = true, default-features = false }
17-
afl = { version = "0.11", optional = true }
18-
elements = { path = "..", features = ["serde"] }
13+
elements = { path = "../", features = [ "serde" ] }
14+
libfuzzer-sys = { version = "0.4.0" }
1915

20-
# Prevent this from interfering with workspaces
21-
[workspace]
22-
members = ["."]
16+
[lints.rust]
17+
unexpected_cfgs = { level = "deny", check-cfg = ['cfg(fuzzing)'] }
2318

24-
[[bin]]
25-
name = "deserialize_transaction"
26-
path = "fuzz_targets/deserialize_transaction.rs"
19+
[lints.clippy]
20+
redundant_clone = "warn"
21+
use_self = "warn"
22+
23+
[package.metadata.rbmt.lint]
24+
allowed_duplicates = [
25+
"hex-conservative",
26+
"getrandom",
27+
"wasi",
28+
]
2729

2830
[[bin]]
2931
name = "deserialize_block"
3032
path = "fuzz_targets/deserialize_block.rs"
33+
test = false
34+
doc = false
35+
bench = false
3136

3237
[[bin]]
3338
name = "deserialize_output"
3439
path = "fuzz_targets/deserialize_output.rs"
40+
test = false
41+
doc = false
42+
bench = false
3543

3644
[[bin]]
3745
name = "deserialize_pset"
3846
path = "fuzz_targets/deserialize_pset.rs"
47+
test = false
48+
doc = false
49+
bench = false
50+
51+
[[bin]]
52+
name = "deserialize_transaction"
53+
path = "fuzz_targets/deserialize_transaction.rs"
54+
test = false
55+
doc = false
56+
bench = false

fuzz/fuzz-util.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
3+
# Sort order is affected by locale. See `man sort`.
4+
# > Set LC_ALL=C to get the traditional sort order that uses native byte values.
5+
export LC_ALL=C
6+
7+
REPO_DIR=$(git rev-parse --show-toplevel)
8+
9+
listTargetFiles() {
10+
pushd "$REPO_DIR/fuzz" > /dev/null || exit 1
11+
find fuzz_targets/ -type f -name "*.rs" | sort
12+
popd > /dev/null || exit 1
13+
}
14+
15+
targetFileToName() {
16+
echo "$1" \
17+
| sed 's/^fuzz_targets\///' \
18+
| sed 's/\.rs$//' \
19+
| sed 's/\//_/g' \
20+
| sed 's/^_//g'
21+
}
22+
23+
# Utility function to avoid CI failures on Windows
24+
checkWindowsFiles() {
25+
incorrectFilenames=$(find . -type f -name "*,*" -o -name "*:*" -o -name "*<*" -o -name "*>*" -o -name "*|*" -o -name "*\?*" -o -name "*\**" -o -name "*\"*" | wc -l)
26+
if [ "$incorrectFilenames" -gt 0 ]; then
27+
echo "Bailing early because there is a Windows-incompatible filename in the tree."
28+
exit 2
29+
fi
30+
}
31+
32+
# Checks whether a fuzz case has artifacts, and dumps them in hex
33+
checkReport() {
34+
artifactDir="fuzz/artifacts/$1"
35+
if [ -d "$artifactDir" ] && [ -n "$(ls -A "$artifactDir" 2>/dev/null)" ]; then
36+
echo "Artifacts found for target: $1"
37+
for artifact in "$artifactDir"/*; do
38+
if [ -f "$artifact" ]; then
39+
echo "Artifact: $(basename "$artifact")"
40+
xxd -p -c10000 < "$artifact"
41+
fi
42+
done
43+
exit 1
44+
fi
45+
}

fuzz/fuzz_targets/deserialize_block.rs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
#![cfg_attr(fuzzing, no_main)]
2+
#![cfg_attr(not(fuzzing), allow(unused))]
13

2-
extern crate elements;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
#[cfg(not(fuzzing))]
7+
fn main() {}
38

49
fn do_test(data: &[u8]) {
510
let block_result: Result<elements::Block, _> = elements::encode::deserialize(data);
@@ -12,25 +17,9 @@ fn do_test(data: &[u8]) {
1217
}
1318
}
1419

15-
#[cfg(feature = "afl")]
16-
extern crate afl;
17-
#[cfg(feature = "afl")]
18-
fn main() {
19-
afl::read_stdio_bytes(|data| {
20-
do_test(&data);
21-
});
22-
}
23-
24-
#[cfg(feature = "honggfuzz")]
25-
#[macro_use] extern crate honggfuzz;
26-
#[cfg(feature = "honggfuzz")]
27-
fn main() {
28-
loop {
29-
fuzz!(|data| {
30-
do_test(data);
31-
});
32-
}
33-
}
20+
fuzz_target!(|data: &[u8]| {
21+
do_test(data);
22+
});
3423

3524
#[cfg(test)]
3625
mod tests {

fuzz/fuzz_targets/deserialize_output.rs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
#![cfg_attr(fuzzing, no_main)]
2+
#![cfg_attr(not(fuzzing), allow(unused))]
13

2-
extern crate elements;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
#[cfg(not(fuzzing))]
7+
fn main() {}
38

49
fn do_test(data: &[u8]) {
510
let result: Result<elements::TxOut, _> = elements::encode::deserialize(data);
@@ -18,25 +23,9 @@ fn do_test(data: &[u8]) {
1823
}
1924
}
2025

21-
#[cfg(feature = "afl")]
22-
extern crate afl;
23-
#[cfg(feature = "afl")]
24-
fn main() {
25-
afl::read_stdio_bytes(|data| {
26-
do_test(&data);
27-
});
28-
}
29-
30-
#[cfg(feature = "honggfuzz")]
31-
#[macro_use] extern crate honggfuzz;
32-
#[cfg(feature = "honggfuzz")]
33-
fn main() {
34-
loop {
35-
fuzz!(|data| {
36-
do_test(data);
37-
});
38-
}
39-
}
26+
fuzz_target!(|data: &[u8]| {
27+
do_test(data);
28+
});
4029

4130
#[cfg(test)]
4231
mod tests {

fuzz/fuzz_targets/deserialize_pset.rs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
#![cfg_attr(fuzzing, no_main)]
2+
#![cfg_attr(not(fuzzing), allow(unused))]
13

2-
extern crate elements;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
#[cfg(not(fuzzing))]
7+
fn main() {}
38

49
fn do_test(data: &[u8]) {
510
let psbt: Result<elements::pset::PartiallySignedTransaction, _> = elements::encode::deserialize(data);
@@ -14,25 +19,9 @@ fn do_test(data: &[u8]) {
1419
}
1520
}
1621

17-
#[cfg(feature = "afl")]
18-
extern crate afl;
19-
#[cfg(feature = "afl")]
20-
fn main() {
21-
afl::read_stdio_bytes(|data| {
22-
do_test(&data);
23-
});
24-
}
25-
26-
#[cfg(feature = "honggfuzz")]
27-
#[macro_use] extern crate honggfuzz;
28-
#[cfg(feature = "honggfuzz")]
29-
fn main() {
30-
loop {
31-
fuzz!(|data| {
32-
do_test(data);
33-
});
34-
}
35-
}
22+
fuzz_target!(|data: &[u8]| {
23+
do_test(data);
24+
});
3625

3726
#[cfg(test)]
3827
mod tests {

fuzz/fuzz_targets/deserialize_transaction.rs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
#![cfg_attr(fuzzing, no_main)]
2+
#![cfg_attr(not(fuzzing), allow(unused))]
13

2-
extern crate elements;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
#[cfg(not(fuzzing))]
7+
fn main() {}
38

49
fn do_test(data: &[u8]) {
510
let tx_result: Result<elements::Transaction, _> = elements::encode::deserialize(data);
@@ -31,25 +36,9 @@ fn do_test(data: &[u8]) {
3136
}
3237
}
3338

34-
#[cfg(feature = "afl")]
35-
extern crate afl;
36-
#[cfg(feature = "afl")]
37-
fn main() {
38-
afl::read_stdio_bytes(|data| {
39-
do_test(&data);
40-
});
41-
}
42-
43-
#[cfg(feature = "honggfuzz")]
44-
#[macro_use] extern crate honggfuzz;
45-
#[cfg(feature = "honggfuzz")]
46-
fn main() {
47-
loop {
48-
fuzz!(|data| {
49-
do_test(data);
50-
});
51-
}
52-
}
39+
fuzz_target!(|data: &[u8]| {
40+
do_test(data);
41+
});
5342

5443
#[cfg(test)]
5544
mod tests {

0 commit comments

Comments
 (0)