Skip to content

Commit e41ca4c

Browse files
committedFeb 17, 2025··
Merge #261: Switch to cargo-fuzz
ae12708 chore: Delete fuzz nix shells (Christian Lewe) 98430c2 chore: Add cargo-fuzz files to .gitignore (Christian Lewe) 6362db7 refactor: Update fuzz scripts (Christian Lewe) 9d05999 refactor: Port fuzz crate to cargo-fuzz (Christian Lewe) 2aa015f chore: Bump MSRV 1.63.0 -> 1.78.0 (Christian Lewe) Pull request description: Adapt the fuzzing infrastructure to use cargo-fuzz. I keep the existing shell scripts because they turned out to be useful. Everything should work as before, except that the `fuzzing` flag is producing warnings when the tests are run. Let's discuss solutions here. ACKs for top commit: apoelstra: ACK ae12708 Tree-SHA512: 0a85e42ff7c08f682e7d4697bb2183a24915d9baa2486d5a7f7bc00d5ab5d20a634fda452efc5209466626c22a3ceff9de2deef736d879e183fb8929c1923917
2 parents fb9d8cf + ae12708 commit e41ca4c

20 files changed

+237
-311
lines changed
 

‎.github/workflows/fuzz.yml

+8-8
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ decode_program,
2323
parse_human,
2424
]
2525
steps:
26-
- name: Install test dependencies
27-
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
28-
2926
- name: Checkout Crate
3027
uses: actions/checkout@v4
3128

@@ -38,15 +35,18 @@ parse_human,
3835
fuzz/target
3936
target
4037
key: cache-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
38+
4139
- name: Install Toolchain
42-
uses: dtolnay/rust-toolchain@stable
40+
uses: dtolnay/rust-toolchain@master
4341
with:
44-
toolchain: '1.65.0'
42+
toolchain: nightly-2024-07-01
43+
components: "llvm-tools-preview"
44+
45+
- name: Install Dependencies
46+
run: cargo update && cargo update -p cc --precise 1.0.83 && cargo install cargo-fuzz
4547

4648
- name: Run Fuzz Target
47-
run: |
48-
echo "Using RUSTFLAGS $RUSTFLAGS"
49-
cd fuzz && cargo update && cargo update -p cc --precise 1.0.83 && ./fuzz.sh "${{ matrix.fuzz_target }}"
49+
run: ./fuzz/fuzz.sh "${{ matrix.fuzz_target }}"
5050

5151
- name: Prepare Artifact
5252
run: echo "${{ matrix.fuzz_target }}" >executed_${{ matrix.fuzz_target }}

‎.github/workflows/main.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
- stable
5555
- beta
5656
- nightly
57-
- 1.63.0
57+
- 1.78.0
5858
steps:
5959
- name: Checkout Crate
6060
uses: actions/checkout@v4

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Cargo.lock
1010
#fuzz
1111
fuzz/hfuzz_target
1212
fuzz/hfuzz_workspace
13+
fuzz/artifacts
14+
fuzz/corpus
1315

1416
#IntelliJ project files
1517
.idea

‎Cargo-recent.lock

+113-82
Large diffs are not rendered by default.

‎Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repository = "https://github.com/BlockstreamResearch/rust-simplicity/"
88
documentation = "https://docs.rs/simplicity-lang/"
99
description = "General purpose library for processing Simplicity programs"
1010
edition = "2021"
11-
rust-version = "1.63.0"
11+
rust-version = "1.78.0"
1212

1313
[features]
1414
default = ["elements"]

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Under development....
55

66
# Minimum Supported Rust Version
77

8-
The MSRV of this crate is **1.63.0**.
8+
The MSRV of this crate is **1.78.0**.
99

1010
# Updating jets code
1111

‎clippy.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
msrv = "1.63.0"
1+
msrv = "1.78.0"
22

33
# Default 250, not sure what units. But it does not like the generic node stuff.
44
type-complexity-threshold = 1000

‎fuzz/Cargo.toml

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "simplicity-fuzz"
33
edition = "2021"
4-
rust-version = "1.63.0"
4+
rust-version = "1.78.0"
55
version = "0.0.1"
66
authors = ["Generated by fuzz/generate-files.sh"]
77
publish = false
@@ -10,21 +10,39 @@ publish = false
1010
cargo-fuzz = true
1111

1212
[dependencies]
13-
honggfuzz = { version = "0.5.55", default-features = false }
13+
libfuzzer-sys = "0.4"
1414
simplicity-lang = { path = "..", features = ["test-utils"] }
1515

16+
[dev-dependencies]
17+
base64 = "0.22.1"
18+
19+
[lints.rust]
20+
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
21+
1622
[[bin]]
1723
name = "c_rust_merkle"
1824
path = "fuzz_targets/c_rust_merkle.rs"
25+
test = false
26+
doc = false
27+
bench = false
1928

2029
[[bin]]
2130
name = "decode_natural"
2231
path = "fuzz_targets/decode_natural.rs"
32+
test = false
33+
doc = false
34+
bench = false
2335

2436
[[bin]]
2537
name = "decode_program"
2638
path = "fuzz_targets/decode_program.rs"
39+
test = false
40+
doc = false
41+
bench = false
2742

2843
[[bin]]
2944
name = "parse_human"
3045
path = "fuzz_targets/parse_human.rs"
46+
test = false
47+
doc = false
48+
bench = false

‎fuzz/cycle.sh

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
# Continuosly cycle over fuzz targets running each for 1 hour.
44
# It uses chrt SCHED_IDLE so that other process takes priority.
5-
#
6-
# For hfuzz options see https://github.com/google/honggfuzz/blob/master/docs/USAGE.md
75

8-
set -e
6+
set -o errexit # exit immediately if any command fails
7+
set -o xtrace # print trace of executed commands
8+
99
REPO_DIR=$(git rev-parse --show-toplevel)
1010
# shellcheck source=./fuzz-util.sh
1111
source "$REPO_DIR/fuzz/fuzz-util.sh"
@@ -14,12 +14,11 @@ while :
1414
do
1515
for targetFile in $(listTargetFiles); do
1616
targetName=$(targetFileToName "$targetFile")
17-
echo "Fuzzing target $targetName ($targetFile)"
1817

1918
# fuzz for one hour
20-
HFUZZ_RUN_ARGS='--run_time 3600' chrt -i 0 cargo hfuzz run "$targetName"
19+
chrt -i 0 cargo-fuzz run "$targetName" -- -max_total_time=3600
2120
# minimize the corpus
22-
HFUZZ_RUN_ARGS="-i hfuzz_workspace/$targetName/input/ -P -M" chrt -i 0 cargo hfuzz run "$targetName"
21+
cargo-fuzz cmin "$targetName"
2322
done
2423
done
2524

‎fuzz/fuzz-util.sh

-28
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,6 @@ targetFileToName() {
1515
| sed 's/\//_/g'
1616
}
1717

18-
targetFileToHFuzzInputArg() {
19-
baseName=$(basename "$1")
20-
dirName="${baseName%.*}"
21-
if [ -d "hfuzz_input/$dirName" ]; then
22-
echo "HFUZZ_INPUT_ARGS=\"-f hfuzz_input/$FILE/input\""
23-
fi
24-
}
25-
2618
listTargetNames() {
2719
for target in $(listTargetFiles); do
2820
targetFileToName "$target"
@@ -37,23 +29,3 @@ checkWindowsFiles() {
3729
exit 2
3830
fi
3931
}
40-
41-
# Checks whether a fuzz case output some report, and dumps it in hex
42-
getReport() {
43-
reportFile="hfuzz_workspace/$1/HONGGFUZZ.REPORT.TXT"
44-
if [ -f "$reportFile" ]; then
45-
cat "$reportFile"
46-
for CASE in "hfuzz_workspace/$1/SIG"*; do
47-
xxd -p -c10000 < "$CASE"
48-
done
49-
return 1
50-
fi
51-
return 0
52-
}
53-
54-
# Check for reports and exit if there are any
55-
checkReport() {
56-
if ! getReport "$1"; then
57-
exit 1
58-
fi
59-
}

‎fuzz/fuzz.sh

+4-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env bash
2-
set -ex
2+
set -o errexit # exit immediately if any command fails
3+
set -o xtrace # print trace of executed commands
34

45
REPO_DIR=$(git rev-parse --show-toplevel)
56

@@ -18,17 +19,8 @@ fi
1819
cargo --version
1920
rustc --version
2021

21-
# Testing
22-
cargo install --force honggfuzz --no-default-features
22+
# Run fuzz target
2323
for targetFile in $targetFiles; do
2424
targetName=$(targetFileToName "$targetFile")
25-
echo "Fuzzing target $targetName ($targetFile)"
26-
if [ -d "hfuzz_input/$targetName" ]; then
27-
HFUZZ_INPUT_ARGS="-f hfuzz_input/$targetName/input\""
28-
else
29-
HFUZZ_INPUT_ARGS=""
30-
fi
31-
HFUZZ_RUN_ARGS="--run_time 30 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run "$targetName"
32-
33-
checkReport "$targetName"
25+
cargo-fuzz run "$targetName" -- -max_total_time=30
3426
done

‎fuzz/fuzz_targets/c_rust_merkle.rs

+17-32
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
use honggfuzz::fuzz;
4-
5-
use simplicity::ffi::tests::{ffi::SimplicityErr, run_program, TestUpTo};
6-
use simplicity::hashes::sha256::Midstate;
7-
use simplicity::jet::Elements;
8-
use simplicity::{BitIter, RedeemNode};
3+
#![cfg_attr(fuzzing, no_main)]
94

5+
#[cfg(any(fuzzing, test))]
106
fn do_test(data: &[u8]) {
7+
use simplicity::ffi::tests::{ffi::SimplicityErr, run_program, TestUpTo};
8+
use simplicity::hashes::sha256::Midstate;
9+
use simplicity::jet::Elements;
10+
use simplicity::{BitIter, RedeemNode};
11+
1112
// To decode the program length, we first try decoding the program using
1213
// `decode_expression` which will not error on a length check. Alternately
1314
// we could decode using RedeemNode::decode and then extract the length
@@ -54,37 +55,21 @@ fn do_test(data: &[u8]) {
5455
}
5556
}
5657

57-
fn main() {
58-
loop {
59-
fuzz!(|data| {
60-
do_test(data);
61-
});
62-
}
63-
}
58+
#[cfg(fuzzing)]
59+
libfuzzer_sys::fuzz_target!(|data| do_test(data));
60+
61+
#[cfg(not(fuzzing))]
62+
fn main() {}
6463

6564
#[cfg(test)]
6665
mod tests {
67-
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
68-
let mut b = 0;
69-
for (idx, c) in hex.as_bytes().iter().enumerate() {
70-
b <<= 4;
71-
match *c {
72-
b'A'..=b'F' => b |= c - b'A' + 10,
73-
b'a'..=b'f' => b |= c - b'a' + 10,
74-
b'0'..=b'9' => b |= c - b'0',
75-
_ => panic!("Bad hex"),
76-
}
77-
if (idx & 1) == 1 {
78-
out.push(b);
79-
b = 0;
80-
}
81-
}
82-
}
66+
use base64::Engine;
8367

8468
#[test]
8569
fn duplicate_crash() {
86-
let mut a = Vec::new();
87-
extend_vec_from_hex("ffffff0000010080800000000000380000001adfc7040000000000000000000007fffffffffffffe1000000000000000000001555600000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff0300000000000000000000000000000000000000000000000000008000000000000000ffffffffffffff7f00000000000000ffffff151515111515155555555555d6eeffffff00", &mut a);
88-
super::do_test(&a);
70+
let data = base64::prelude::BASE64_STANDARD
71+
.decode("Cg==")
72+
.expect("base64 should be valid");
73+
super::do_test(&data);
8974
}
9075
}

‎fuzz/fuzz_targets/decode_natural.rs

+15-30
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
use honggfuzz::fuzz;
4-
5-
use simplicity::encode_natural;
6-
use simplicity::{BitIter, BitWriter};
3+
#![cfg_attr(fuzzing, no_main)]
74

5+
#[cfg(any(fuzzing, test))]
86
fn do_test(data: &[u8]) {
7+
use simplicity::encode_natural;
8+
use simplicity::{BitIter, BitWriter};
9+
910
let mut iter = BitIter::new(data.iter().cloned());
1011

1112
if let Ok(natural) = iter.read_natural(None) {
@@ -28,37 +29,21 @@ fn do_test(data: &[u8]) {
2829
}
2930
}
3031

31-
fn main() {
32-
loop {
33-
fuzz!(|data| {
34-
do_test(data);
35-
});
36-
}
37-
}
32+
#[cfg(fuzzing)]
33+
libfuzzer_sys::fuzz_target!(|data| do_test(data));
34+
35+
#[cfg(not(fuzzing))]
36+
fn main() {}
3837

3938
#[cfg(test)]
4039
mod tests {
41-
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
42-
let mut b = 0;
43-
for (idx, c) in hex.as_bytes().iter().enumerate() {
44-
b <<= 4;
45-
match *c {
46-
b'A'..=b'F' => b |= c - b'A' + 10,
47-
b'a'..=b'f' => b |= c - b'a' + 10,
48-
b'0'..=b'9' => b |= c - b'0',
49-
_ => panic!("Bad hex"),
50-
}
51-
if (idx & 1) == 1 {
52-
out.push(b);
53-
b = 0;
54-
}
55-
}
56-
}
40+
use base64::Engine;
5741

5842
#[test]
5943
fn duplicate_crash() {
60-
let mut a = Vec::new();
61-
extend_vec_from_hex("00000", &mut a);
62-
super::do_test(&a);
44+
let data = base64::prelude::BASE64_STANDARD
45+
.decode("Cg==")
46+
.expect("base64 should be valid");
47+
super::do_test(&data);
6348
}
6449
}

‎fuzz/fuzz_targets/decode_program.rs

+15-30
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
use honggfuzz::fuzz;
4-
5-
use simplicity::jet::Core;
6-
use simplicity::{BitIter, BitWriter, RedeemNode};
3+
#![cfg_attr(fuzzing, no_main)]
74

5+
#[cfg(any(fuzzing, test))]
86
fn do_test(data: &[u8]) {
7+
use simplicity::jet::Core;
8+
use simplicity::{BitIter, BitWriter, RedeemNode};
9+
910
let prog_iter = BitIter::new(data.iter().cloned());
1011
let wit_iter = BitIter::new(core::iter::repeat(0));
1112
if let Ok(program) = RedeemNode::<Core>::decode(prog_iter, wit_iter) {
@@ -24,37 +25,21 @@ fn do_test(data: &[u8]) {
2425
}
2526
}
2627

27-
fn main() {
28-
loop {
29-
fuzz!(|data| {
30-
do_test(data);
31-
});
32-
}
33-
}
28+
#[cfg(fuzzing)]
29+
libfuzzer_sys::fuzz_target!(|data| do_test(data));
30+
31+
#[cfg(not(fuzzing))]
32+
fn main() {}
3433

3534
#[cfg(test)]
3635
mod tests {
37-
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
38-
let mut b = 0;
39-
for (idx, c) in hex.as_bytes().iter().enumerate() {
40-
b <<= 4;
41-
match *c {
42-
b'A'..=b'F' => b |= c - b'A' + 10,
43-
b'a'..=b'f' => b |= c - b'a' + 10,
44-
b'0'..=b'9' => b |= c - b'0',
45-
_ => panic!("Bad hex"),
46-
}
47-
if (idx & 1) == 1 {
48-
out.push(b);
49-
b = 0;
50-
}
51-
}
52-
}
36+
use base64::Engine;
5337

5438
#[test]
5539
fn duplicate_crash() {
56-
let mut a = Vec::new();
57-
extend_vec_from_hex("00000", &mut a);
58-
super::do_test(&a);
40+
let data = base64::prelude::BASE64_STANDARD
41+
.decode("Cg==")
42+
.expect("base64 should be valid");
43+
super::do_test(&data);
5944
}
6045
}

‎fuzz/fuzz_targets/parse_human.rs

+16-31
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
use honggfuzz::fuzz;
4-
use simplicity::human_encoding::Forest;
5-
use simplicity::jet::Elements;
6-
use std::str;
3+
#![cfg_attr(fuzzing, no_main)]
74

5+
#[cfg(any(fuzzing, test))]
86
fn do_test(data: &[u8]) {
9-
let s = match str::from_utf8(data) {
7+
use simplicity::human_encoding::Forest;
8+
use simplicity::jet::Elements;
9+
10+
let s = match std::str::from_utf8(data) {
1011
Ok(s) => s,
1112
Err(_) => return,
1213
};
@@ -18,37 +19,21 @@ fn do_test(data: &[u8]) {
1819
}
1920
}
2021

21-
fn main() {
22-
loop {
23-
fuzz!(|data| {
24-
do_test(data);
25-
});
26-
}
27-
}
22+
#[cfg(fuzzing)]
23+
libfuzzer_sys::fuzz_target!(|data| do_test(data));
24+
25+
#[cfg(not(fuzzing))]
26+
fn main() {}
2827

2928
#[cfg(test)]
3029
mod tests {
31-
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
32-
let mut b = 0;
33-
for (idx, c) in hex.as_bytes().iter().enumerate() {
34-
b <<= 4;
35-
match *c {
36-
b'A'..=b'F' => b |= c - b'A' + 10,
37-
b'a'..=b'f' => b |= c - b'a' + 10,
38-
b'0'..=b'9' => b |= c - b'0',
39-
_ => panic!("Bad hex"),
40-
}
41-
if (idx & 1) == 1 {
42-
out.push(b);
43-
b = 0;
44-
}
45-
}
46-
}
30+
use base64::Engine;
4731

4832
#[test]
4933
fn duplicate_crash() {
50-
let mut a = Vec::new();
51-
extend_vec_from_hex("00", &mut a);
52-
super::do_test(&a);
34+
let data = base64::prelude::BASE64_STANDARD
35+
.decode("Cg==")
36+
.expect("base64 should be valid");
37+
super::do_test(&data);
5338
}
5439
}

‎fuzz/generate-files.sh

+16-10
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ cat > "$REPO_DIR/fuzz/Cargo.toml" <<EOF
1212
[package]
1313
name = "simplicity-fuzz"
1414
edition = "2021"
15-
rust-version = "1.63.0"
15+
rust-version = "1.78.0"
1616
version = "0.0.1"
1717
authors = ["Generated by fuzz/generate-files.sh"]
1818
publish = false
@@ -21,8 +21,11 @@ publish = false
2121
cargo-fuzz = true
2222
2323
[dependencies]
24-
honggfuzz = { version = "0.5.55", default-features = false }
24+
libfuzzer-sys = "0.4"
2525
simplicity-lang = { path = "..", features = ["test-utils"] }
26+
27+
[dev-dependencies]
28+
base64 = "0.22.1"
2629
EOF
2730

2831
for targetFile in $(listTargetFiles); do
@@ -32,6 +35,9 @@ for targetFile in $(listTargetFiles); do
3235
[[bin]]
3336
name = "$targetName"
3437
path = "$targetFile"
38+
test = false
39+
doc = false
40+
bench = false
3541
EOF
3642
done
3743

@@ -59,9 +65,6 @@ jobs:
5965
$(for name in $(listTargetNames); do echo "$name,"; done)
6066
]
6167
steps:
62-
- name: Install test dependencies
63-
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
64-
6568
- name: Checkout Crate
6669
uses: actions/checkout@v4
6770
@@ -74,15 +77,18 @@ $(for name in $(listTargetNames); do echo "$name,"; done)
7477
fuzz/target
7578
target
7679
key: cache-\${{ matrix.target }}-\${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
80+
7781
- name: Install Toolchain
78-
uses: dtolnay/rust-toolchain@stable
82+
uses: dtolnay/rust-toolchain@master
7983
with:
80-
toolchain: '1.65.0'
84+
toolchain: nightly-2024-07-01
85+
components: "llvm-tools-preview"
86+
87+
- name: Install Dependencies
88+
run: cargo update && cargo update -p cc --precise 1.0.83 && cargo install cargo-fuzz
8189
8290
- name: Run Fuzz Target
83-
run: |
84-
echo "Using RUSTFLAGS \$RUSTFLAGS"
85-
cd fuzz && cargo update && cargo update -p cc --precise 1.0.83 && ./fuzz.sh "\${{ matrix.fuzz_target }}"
91+
run: ./fuzz/fuzz.sh "\${{ matrix.fuzz_target }}"
8692
8793
- name: Prepare Artifact
8894
run: echo "\${{ matrix.fuzz_target }}" >executed_\${{ matrix.fuzz_target }}

‎fuzz/honggfuzz-rs.nix

-20
This file was deleted.

‎fuzz/shell.nix

-14
This file was deleted.

‎simplicity-sys/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ repository = "https://github.com/BlockstreamResearch/rust-simplicity/"
77
documentation = "https://docs.rs/simplicity-sys/"
88
description = "FFI bindings to libsimplicity"
99
edition = "2021"
10-
rust-version = "1.63.0"
10+
rust-version = "1.78.0"
1111

1212
[build-dependencies]
1313
cc = "1.0.83"

‎simplicity-sys/src/c_jets/c_frame.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl CFrameItem {
5353

5454
/// Number of UWORDs required to hold n bits
5555
pub fn uword_width(n_bits: usize) -> usize {
56-
(n_bits + 8 * mem::size_of::<UWORD>() - 1) / (8 * mem::size_of::<UWORD>())
56+
n_bits.div_ceil(8 * mem::size_of::<UWORD>())
5757
}
5858

5959
/// Number of bytes required to hold n bits

0 commit comments

Comments
 (0)
Please sign in to comment.