rust-bitcoin-unsafe-fast/fuzz
Tobin C. Harding 6ab0110964
Run fuzzer daily
Waiting for the fuzzer slows down the dev feedback loop because it makes
CI slow. We still want fuzz coverage but not in a way that slows down
devs.

Instead of running the fuzzer on every PR just run it once a nightly.

As we do for other daily jobs, rename the yaml file to make explicit
what it does.

Open questions:

- This should run for 30 minutes but I can't work out why the current
  set up only runs for a shorter time than that?
- Is this less fuzzing i.e., is 30 minutes once a day better or worse that 1
  minute 30 times?
2024-03-27 04:35:23 +11:00
..
fuzz_targets Add bitcoin-units crate 2023-12-11 08:52:31 +11:00
hfuzz_input move bitcoin/fuzz into repo root; add to workspace 2023-04-27 00:24:48 +00:00
.gitignore move bitcoin/fuzz into repo root; add to workspace 2023-04-27 00:24:48 +00:00
Cargo.toml fuzz: Use path in manifest instead of version 2024-03-21 16:20:03 +11:00
README.md add README note explaining how to disable crypto for fuzzing 2023-05-01 21:32:09 +00:00
cycle.sh Fix typos 2024-01-05 23:10:52 +08:00
fuzz-util.sh ci: replace fuzz toolchain 2023-10-10 14:39:15 -03:00
fuzz.sh Run fuzzer daily 2024-03-27 04:35:23 +11:00
generate-files.sh Add rust-version to the workspace manifests 2023-11-23 06:20:04 +11:00

README.md

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.

Fuzzing with weak cryptography

You may wish to replace the hashing and signing code with broken crypto, which will be faster and enable the fuzzer to do otherwise impossible things such as forging signatures or finding preimages to hashes.

Doing so may result in spurious bug reports since the broken crypto does not respect the encoding or algebraic invariants upheld by the real crypto. We would like to improve this but it's a nontrivial problem -- though not beyond the abilities of a motivated student with a few months of time. Please let us know if you are interested in taking this on!

Meanwhile, to use the broken crypto, simply compile (and run the fuzzing scripts) with

RUSTFLAGS="--cfg=hashes_fuzz --cfg=secp256k1_fuzz"

which will replace the hashing library with broken hashes, and the secp256k1 library with broken cryptography.

Needless to say, NEVER COMPILE REAL CODE WITH THESE FLAGS because if a fuzzer can break your crypto, so can anybody.

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

cargo test

Note that if you set your RUSTFLAGS while fuzzing (see above) you must make sure they are set the same way when running cargo test.