Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1# Fuzzing Bitcoin Core using libFuzzer3## Quickstart guide5To quickly get started fuzzing Bitcoin Core using [libFuzzer](https://llvm.org/docs/LibFuzzer.html):7```sh8$ git clone https://github.com/bitcoin/bitcoin9$ cd bitcoin/10$ cmake --preset=libfuzzer11$ cmake --build build_fuzz12$ FUZZ=process_message build_fuzz/bin/fuzz13# abort fuzzing using ctrl-c14```16One can use `--preset=libfuzzer-nosan` to do the same without common sanitizers enabled.17Note that this preset uses a different build directory, so replace `build_fuzz` with18`build_fuzz_nosan` in the build and run commands above.19See [further](#run-without-sanitizers-for-increased-throughput) for more information.21There is also a runner script to execute all fuzz targets. Refer to22`./build_fuzz/test/fuzz/test_runner.py --help` for more details.24For source-based coverage reports, see [developer notes](/doc/developer-notes.md#compiling-for-fuzz-coverage).26macOS users: We recommend fuzzing on Linux, see [macOS notes](#macos-notes) for27more information.29## Overview of Bitcoin Core fuzzing31[Google](https://github.com/google/fuzzing/) has a good overview of fuzzing in general, with contributions from key architects of some of the most-used fuzzers. [This paper](https://agroce.github.io/bitcoin_report.pdf) includes an external overview of the status of Bitcoin Core fuzzing, as of summer 2021. [John Regehr](https://blog.regehr.org/archives/1687) provides good advice on writing code that assists fuzzers in finding bugs, which is useful for developers to keep in mind.33## Fuzzing harnesses and output35[`process_message`](/src/test/fuzz/process_message.cpp) is a fuzzing harness for the [`ProcessMessage(...)` function (`net_processing`)](/src/net_processing.cpp). The available fuzzing harnesses are found in [`src/test/fuzz/`](/src/test/fuzz).37The fuzzer will output `NEW` every time it has created a test input that covers new areas of the code under test. For more information on how to interpret the fuzzer output, see the [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html).39If you specify a corpus directory then any new coverage increasing inputs will be saved there:41```sh42$ mkdir -p process_message-seeded-from-thin-air/43$ FUZZ=process_message build_fuzz/bin/fuzz process_message-seeded-from-thin-air/44INFO: Seed: 84052229245INFO: Loaded 1 modules (424174 inline 8-bit counters): 424174 [0x55e121ef9ab8, 0x55e121f613a6),46INFO: Loaded 1 PC tables (424174 PCs): 424174 [0x55e121f613a8,0x55e1225da288),47INFO: 0 files found in process_message-seeded-from-thin-air/48INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes49INFO: A corpus is not provided, starting from an empty corpus50#2 INITED cov: 94 ft: 95 corp: 1/1b exec/s: 0 rss: 150Mb51#3 NEW cov: 95 ft: 96 corp: 2/3b lim: 4 exec/s: 0 rss: 150Mb L: 2/2 MS: 1 InsertByte-52#4 NEW cov: 96 ft: 98 corp: 3/7b lim: 4 exec/s: 0 rss: 150Mb L: 4/4 MS: 1 CrossOver-53#21 NEW cov: 96 ft: 100 corp: 4/11b lim: 4 exec/s: 0 rss: 150Mb L: 4/4 MS: 2 ChangeBit-CrossOver-54#324 NEW cov: 101 ft: 105 corp: 5/12b lim: 6 exec/s: 0 rss: 150Mb L: 6/6 MS: 5 CrossOver-ChangeBit-CopyPart-ChangeBit-ChangeBinInt-55#1239 REDUCE cov: 102 ft: 106 corp: 6/24b lim: 14 exec/s: 0 rss: 150Mb L: 13/13 MS: 5 ChangeBit-CrossOver-EraseBytes-ChangeBit-InsertRepeatedBytes-56#1272 REDUCE cov: 102 ft: 106 corp: 6/23b lim: 14 exec/s: 0 rss: 150Mb L: 12/12 MS: 3 ChangeBinInt-ChangeBit-EraseBytes-57NEW_FUNC[1/677]: 0x55e11f456690 in std::_Function_base::~_Function_base() /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/std_function.h:25558NEW_FUNC[2/677]: 0x55e11f465800 in CDataStream::CDataStream(std::vector<unsigned char, std::allocator<unsigned char> > const&, int, int) src/./streams.h:24859#2125 REDUCE cov: 4820 ft: 4867 corp: 7/29b lim: 21 exec/s: 0 rss: 155Mb L: 6/12 MS: 2 CopyPart-CMP- DE: "block"-60NEW_FUNC[1/9]: 0x55e11f64d790 in std::_Rb_tree<uint256, std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > >, std::_Select1st<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > >, std::less<uint256>, std::allocator<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > > >::~_Rb_tree() /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_tree.h:97261NEW_FUNC[2/9]: 0x55e11f64d870 in std::_Rb_tree<uint256, std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > >, std::_Select1st<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > >, std::less<uint256>, std::allocator<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > > >::_M_erase(std::_Rb_tree_node<std::pair<uint256 const, std::chrono::duration<long, std::ratio<1l, 1000000l> > > >*) /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_tree.h:187562#2228 NEW cov: 4898 ft: 4971 corp: 8/35b lim: 21 exec/s: 0 rss: 156Mb L: 6/12 MS: 3 EraseBytes-CopyPart-PersAutoDict- DE: "block"-63NEW_FUNC[1/5]: 0x55e11f46df70 in std::enable_if<__and_<std::allocator_traits<zero_after_free_allocator<char> >::__construct_helper<char, unsigned char const&>::type>::value, void>::type std::allocator_traits<zero_after_free_allocator<char> >::_S_construct<char, unsigned char const&>(zero_after_free_allocator<char>&, char*, unsigned char const&) /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/alloc_traits.h:24364NEW_FUNC[2/5]: 0x55e11f477390 in std::vector<unsigned char, std::allocator<unsigned char> >::data() /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_vector.h:105665#2456 NEW cov: 4933 ft: 5042 corp: 9/55b lim: 21 exec/s: 0 rss: 160Mb L: 20/20 MS: 3 ChangeByte-InsertRepeatedBytes-PersAutoDict- DE: "block"-66#2467 NEW cov: 4933 ft: 5043 corp: 10/76b lim: 21 exec/s: 0 rss: 161Mb L: 21/21 MS: 1 InsertByte-67#4215 NEW cov: 4941 ft: 5129 corp: 17/205b lim: 29 exec/s: 4215 rss: 350Mb L: 29/29 MS: 5 InsertByte-ChangeBit-CopyPart-InsertRepeatedBytes-CrossOver-68#4567 REDUCE cov: 4941 ft: 5129 corp: 17/204b lim: 29 exec/s: 4567 rss: 404Mb L: 24/29 MS: 2 ChangeByte-EraseBytes-69#6642 NEW cov: 4941 ft: 5138 corp: 18/244b lim: 43 exec/s: 2214 rss: 450Mb L: 43/43 MS: 3 CopyPart-CMP-CrossOver- DE: "verack"-70# abort fuzzing using ctrl-c71$ ls process_message-seeded-from-thin-air/72349ac589fc66a09abc0b72bb4ae445a7a19e2cd8 4df479f1f421f2ea64b383cd4919a272604087a773a640312c98dcc55d6744730c33e41c5168c55f09 b135de16e4709558c0797c15f86046d31c5d86d774c000f7b41b05139de8b63f4cbf7d1ad4c6e2aa7f fc52cc00ec1eb1c08470e69f809ae4993fa7008275$ cat --show-nonprinting process_message-seeded-from-thin-air/349ac589fc66a09abc0b72bb4ae445a7a19e2cd876block^@M-^?M-^?M-^?M-^?M-^?nM-^?M-^?77```79In this case the fuzzer managed to create a `block` message which when passed to `ProcessMessage(...)` increased coverage.81It is possible to specify `bitcoind` arguments to the `fuzz` executable.82Depending on the test, they may be ignored or consumed and alter the behavior83of the test. Just make sure to use double-dash to distinguish them from the84fuzzer's own arguments:86```sh87$ FUZZ=address_deserialize build_fuzz/bin/fuzz -runs=1 fuzz_corpora/address_deserialize --checkaddrman=5 --printtoconsole=188```90## Fuzzing corpora92The project's collection of seed corpora is found in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo.94To fuzz `process_message` using the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) seed corpus:96```sh97$ git clone --depth=1 https://github.com/bitcoin-core/qa-assets98$ FUZZ=process_message build_fuzz/bin/fuzz qa-assets/fuzz_corpora/process_message/99INFO: Seed: 1346407872100INFO: Loaded 1 modules (424174 inline 8-bit counters): 424174 [0x55d8a9004ab8, 0x55d8a906c3a6),101INFO: Loaded 1 PC tables (424174 PCs): 424174 [0x55d8a906c3a8,0x55d8a96e5288),102INFO: 991 files found in qa-assets/fuzz_corpora/process_message/103INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes104INFO: seed corpus: files: 991 min: 1b max: 1858b total: 288291b rss: 150Mb105#993 INITED cov: 7063 ft: 8236 corp: 25/3821b exec/s: 0 rss: 181Mb106…107```109## Using the MemorySanitizer (MSan)111MSan [requires](https://clang.llvm.org/docs/MemorySanitizer.html#handling-external-code)112that all linked code be instrumented. The exact steps to achieve this may vary113but involve compiling `clang` from source, using the built `clang` to compile114an instrumentalized libc++, then using it to build [Bitcoin Core dependencies115from source](../depends/README.md) and finally the Bitcoin Core fuzz binary116itself. One can use the MSan CI job as an example for how to perform these117steps.119Valgrind is an alternative to MSan that does not require building a custom libc++.121## Run without sanitizers for increased throughput123Fuzzing on a harness compiled with `-DSANITIZERS=address,fuzzer,undefined` is124good for finding bugs. However, the very slow execution even under libFuzzer125will limit the ability to find new coverage. A good approach is to perform126occasional long runs without the additional bug-detectors127(`--preset=libfuzzer-nosan`) and then merge new inputs into a corpus as described in128the qa-assets repo129(https://github.com/bitcoin-core/qa-assets/blob/main/.github/PULL_REQUEST_TEMPLATE.md).130Patience is useful; even with improved throughput, libFuzzer may need days and13110s of millions of executions to reach deep/hard targets.133## Reproduce a fuzzer crash reported by the CI135- `cd` into the `qa-assets` directory and update it with `git pull qa-assets`136- locate the crash case described in the CI output, e.g. `Test unit written to137./crash-1bc91feec9fc00b107d97dc225a9f2cdaa078eb6`138- make sure to compile with all sanitizers, if they are needed (fuzzing runs139more slowly with sanitizers enabled, but a crash should be reproducible very140quickly from a crash case)141- run the fuzzer with the case number appended to the seed corpus path:142`FUZZ=process_message build_fuzz/bin/fuzz143qa-assets/fuzz_corpora/process_message/1bc91feec9fc00b107d97dc225a9f2cdaa078eb6`144- If the file does not exist, make sure you are checking out the exact same commit id145for the qa-assets repo. If the file was found while running the fuzz engine in the CI,146you should be able to reproduce the crash locally with the same (or a similar input)147within a few minutes. Alternatively, you can use the base64 encoded file from the CI log,148if it exists. e.g.149`echo "Nb6Fc/97AACAAAD/ewAAgAAAAIAAAACAAAAAoA==" |150base64 --decode > qa-assets/fuzz_corpora/process_message/1bc91feec9fc00b107d97dc225a9f2cdaa078eb6`152## Submit improved coverage154If you find coverage increasing inputs when fuzzing you are highly encouraged to submit them for inclusion in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo.156Every single pull request submitted against the Bitcoin Core repo is automatically tested against all inputs in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. Contributing new coverage increasing inputs is an easy way to help make Bitcoin Core more robust.158## Building and debugging fuzz tests160There are 3 ways fuzz tests can be built:1621. With `-DBUILD_FOR_FUZZING=ON` which forces on fuzz determinism (skipping163proof of work checks, disabling random number seeding, disabling clock time)164and causes `Assume()` checks to abort on failure.166This is the normal way to run fuzz tests and generate new inputs. Because167determinism is hardcoded on in this build, only the fuzz binary can be built168and all other binaries are disabled.1702. With `-DBUILD_FUZZ_BINARY=ON -DCMAKE_BUILD_TYPE=Debug` which causes171`Assume()` checks to abort on failure, and enables fuzz determinism, but172makes it optional.174Determinism is turned on in the fuzz binary by default, but can be turned off175by setting the `FUZZ_NONDETERMINISM` environment variable to any value, which176may be useful for running fuzz tests with code that deterministic execution177would otherwise skip.179Since `BUILD_FUZZ_BINARY`, unlike `BUILD_FOR_FUZZING`, does not hardcode on180determinism, this allows non-fuzz binaries to coexist in the same build,181making it possible to reproduce fuzz test failures in a normal build.1833. With `-DBUILD_FUZZ_BINARY=ON -DCMAKE_BUILD_TYPE=Release`. In this build, the184fuzz binary will build but refuse to run, because in release builds185determinism is forced off and `Assume()` checks do not abort, so running the186tests would not be useful. This build is only useful for ensuring fuzz tests187compile and link.189## macOS notes191Support for fuzzing on macOS is not officially maintained by this project. If192you are running into issues on macOS, we recommend fuzzing on Linux instead for193best results. On macOS this can be done within Docker or a virtual machine.195Reproducing and debugging fuzz testcases on macOS is supported, by building the196fuzz binary without support for any specific fuzzing engine.198# Fuzzing Bitcoin Core using afl++200## Quickstart guide202To quickly get started fuzzing Bitcoin Core using [afl++](https://github.com/AFLplusplus/AFLplusplus):204```sh205$ git clone https://github.com/bitcoin/bitcoin206$ cd bitcoin/207$ git clone https://github.com/AFLplusplus/AFLplusplus208$ make -C AFLplusplus/ source-only209# If afl-clang-lto is not available, see210# https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/fuzzing_in_depth.md#a-selecting-the-best-afl-compiler-for-instrumenting-the-target211$ cmake -B build_fuzz \212-DCMAKE_C_COMPILER="$(pwd)/AFLplusplus/afl-clang-lto" \213-DCMAKE_CXX_COMPILER="$(pwd)/AFLplusplus/afl-clang-lto++" \214-DBUILD_FOR_FUZZING=ON215$ cmake --build build_fuzz216$ mkdir -p inputs/ outputs/217$ echo A > inputs/thin-air-input218$ FUZZ=bech32 ./AFLplusplus/afl-fuzz -i inputs/ -o outputs/ -- build_fuzz/bin/fuzz219# You may have to change a few kernel parameters to test optimally - afl-fuzz220# will print an error and suggestion if so.221```223Read the [afl++ documentation](https://github.com/AFLplusplus/AFLplusplus) for more information.225# Fuzzing Bitcoin Core using Honggfuzz227## Quickstart guide229To quickly get started fuzzing Bitcoin Core using [Honggfuzz](https://github.com/google/honggfuzz):231```sh232$ git clone https://github.com/bitcoin/bitcoin233$ cd bitcoin/234$ git clone https://github.com/google/honggfuzz235$ cd honggfuzz/236$ make237$ cd ..238$ cmake -B build_fuzz \239-DCMAKE_C_COMPILER="$(pwd)/honggfuzz/hfuzz_cc/hfuzz-clang" \240-DCMAKE_CXX_COMPILER="$(pwd)/honggfuzz/hfuzz_cc/hfuzz-clang++" \241-DBUILD_FOR_FUZZING=ON \242-DSANITIZERS=address,undefined243$ cmake --build build_fuzz244$ mkdir -p inputs/245$ FUZZ=process_message ./honggfuzz/honggfuzz -i inputs/ -- build_fuzz/bin/fuzz246```248Read the [Honggfuzz documentation](https://github.com/google/honggfuzz/blob/master/docs/USAGE.md) for more information.250# OSS-Fuzz
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.