Skip to content

bitcoinfuzz/bitcoinfuzz

Repository files navigation

bitcoinfuzz

Differential Fuzzing of Bitcoin implementations and libraries. Note this project is a WIP and might be not stable.

Installation

Platform Support

bitcoinfuzz is developed and supported on Linux only.

MacOS or Windows Users:

We recommend using Docker to run bitcoinfuzz if you're on macOS or Windows. This provides the most reliable fuzzing experience with full toolchain support.

Dependencies

llvm toolset (clang and libfuzzer)

  • To support the flags used in some modules -fsanitize=address,fuzzer -std=c++20 the minimum clang version required is 10.0

  • For ubuntu/debian it can be installed using the package manager:

    sudo apt install clang lld llvm-dev
    
  • To install it from source check clang_get_started. You must build it with this cmake option: -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt"

Running

The fuzzer has a lot of dependencies from the number of projects (and their runtime/lang) it supports and for that reason we provide a docker workflow to ease the burden of setting up and building the project.

See RUNNING.md to understand better the options and their configurations.

If you ended up luckily finding more bugs report them responsibly (see the respective SECURITY.md file on the project repo). You can merge your fuzzing corpus with our public corpora.

Fuzzing with AFL++

To quickly get started fuzzing using afl++:

$ git clone https://github.com/AFLplusplus/AFLplusplus
$ make -C AFLplusplus/ source-only
# To choose/use any other afl compiler, see
# https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/fuzzing_in_depth.md#a-selecting-the-best-afl-compiler-for-instrumenting-the-target
$ CC="AFLplusplus/afl-clang-fast" CXX="AFLplusplus/afl-clang-fast++" make
$ mkdir -p inputs/ outputs/
$ echo A > inputs/thin-air-input
$ FUZZ=addrv2 ./AFLplusplus/afl-fuzz -i inputs/ -o outputs/ -- ./bitcoinfuzz

Read the afl++ documentation for more information.

Build Options

You can build the modules in two ways: manual or automatic. The automatic method is provided by the auto_build.py script, which simplifies the build and clean processes. Additionally, you can use Docker or Docker Compose to run the application without installing dependencies directly on your machine.

Automatic Method: auto_build.py

The auto_build.py script allows you to automatically build the modules based on the flags defined in CXXFLAGS. It also provides options to clean the builds before compiling.

How to use:

  1. Automatic Build:

    • To automatically build the modules, define the flags in CXXFLAGS and run the script:
      CXXFLAGS="-DLDK -DLND" ./auto_build.py
      This will automatically build the LDK and LND modules.
  2. Automatic Clean:

    • The script supports three cleaning modes before building:
      • Full Clean: Cleans all modules before building the selected ones.
        CLEAN_BUILD="FULL" CXXFLAGS="-DLDK -DLND" ./auto_build.py
      • Clean: Cleans only the modules that will be built based on CXXFLAGS.
        CLEAN_BUILD="CLEAN" CXXFLAGS="-DLDK -DLND" ./auto_build.py
      • Select Clean: Cleans specific modules defined in CLEAN_BUILD, regardless of CXXFLAGS.
        CLEAN_BUILD="-DLDK -DBTCD" CXXFLAGS="-DLDK -DLND" ./auto_build.py
        In this case, the script will run make clean for LDK and BTCD, but will only build the modules defined in CXXFLAGS (LDK and LND).

Docker

See RUNNING.md for more information.

Manual Method

If you prefer, you can still build the modules manually. Below are the steps for each module:

Bitcoin modules:

  • cd modules/rustbitcoin
    make
    export CXXFLAGS="$CXXFLAGS -DRUST_BITCOIN"
  • cd modules/tinyminiscript
    make
    export CXXFLAGS="$CXXFLAGS -DTINY_MINISCRIPT"
  • cd modules/rustminiscript
    make
    export CXXFLAGS="$CXXFLAGS -DRUST_MINISCRIPT"
  • cd modules/btcd
    make
    export CXXFLAGS="$CXXFLAGS -DBTCD"
  • gocoin is a full Bitcoin node implementation written in Go.

    cd modules/gocoin
    make
    export CXXFLAGS="$CXXFLAGS -DGOCOIN"

    Note: gocoin cannot be used together with btcd in the same build due to cgo symbol conflicts (both embed the Go runtime).

  • cd modules/nbitcoin
    make
    export CXXFLAGS="$CXXFLAGS -DNBITCOIN"
  • To run the fuzzer with embit module, you need to install the embit library.

    To install the embit library, you can use the following command:

    cd modules/embit
    pip install -r ./requirements.txt
    cd modules/embit
    make
    export CXXFLAGS="$CXXFLAGS -DEMBIT"
  • cd modules/rustbitcoinkernel
    make
    export CXXFLAGS="$CXXFLAGS -DRUSTBITCOINKERNEL"
  • To run the fuzzer with py-bitcoinkernel module, you need to install the py-bitcoinkernel library.

    To install the py-bitcoinkernel library, you can use the following command:

    cd modules/pybitcoinkernel
    pip install -r ./requirements.txt
    cd modules/pybitcoinkernel
    make
    export CXXFLAGS="$CXXFLAGS -DPYBITCOINKERNEL"
  • bitcoinkernel (Bitcoin Core's libbitcoinkernel project)

    Use this module when you want to run kernel fuzz targets against a single libbitcoinkernel version.

    • Default version: master
    • Pin a version: set BITCOINKERNEL_REF (tag/branch/commit)
    # build manual (default: master)
    cd modules/bitcoinkernel
    BITCOINKERNEL_REF=[ref]
    make
    export CXXFLAGS="$CXXFLAGS -DBITCOINKERNEL"
  • bitcoinkernel-variant (differential fuzzing for libbitcoinkernel)

    For differential fuzzing between two bitcoinkernel versions, build two modules:

    • BITCOINKERNEL (base; BITCOINKERNEL_REF, default master)
    • BITCOINKERNEL_VARIANT (comparison; BITCOINKERNEL_VARIANT_REF, default master)
    # 1) base (creates the reference repo at modules/bitcoinkernel/bitcoin)
    cd modules/bitcoinkernel
    BITCOINKERNEL_REF=[base_ref]
    make
    export CXXFLAGS="$CXXFLAGS -DBITCOINKERNEL"
    
    # 2) variant (ref repo is required for build)
    cd ../bitcoinkernelvariant
    BITCOINKERNEL_VARIANT_REF=<variant_ref>
    make
    export CXXFLAGS="$CXXFLAGS -DBITCOINKERNEL_VARIANT"
  • cd modules/bitcoin
    make
    export CXXFLAGS="$CXXFLAGS -DBITCOIN_CORE"
  • cd modules/bitcoinj
    make
    export CXXFLAGS="$CXXFLAGS -DBITCOINJ"
  • git submodule update --init --recursive external/libwally-core
    cd modules/libwallycore
    make
    export CXXFLAGS="$CXXFLAGS -DLIBWALLY_CORE"

Lightning modules:

  • cd modules/ldk
    make
    export CXXFLAGS="$CXXFLAGS -DLDK"
  • cd modules/lnd
    make
    export CXXFLAGS="$CXXFLAGS -DLND"
  • cd modules/nlightning
    make
    export CXXFLAGS="$CXXFLAGS -DNLIGHTNING"
  • pip install mako
    git submodule update --init --recursive external/lightning
    cd modules/clightning
    make
    export CXXFLAGS="$CXXFLAGS -DCLIGHTNING"
  • git submodule update --init --recursive external/eclair
    cd modules/eclair
    make
    export CXXFLAGS="$CXXFLAGS -DECLAIR"

    Note: When fuzzing with Eclair, the JVM's JIT compiler allocates memory that LSan cannot track through the standard allocator, causing false-positive leak reports. Use the provided suppressions file to silence them:

    LSAN_OPTIONS="suppressions=$(pwd)/lsan.supp" FUZZ=deserialize_invoice ./bitcoinfuzz
    LSAN_OPTIONS="suppressions=$(pwd)/lsan.supp" FUZZ=deserialize_offer ./bitcoinfuzz

    This suppressions file was only validated on Ubuntu and might not work depending on your setup. You can write your own file or disable the leak detection globally.

  • cd modules/lightningkmp
    make
    export CXXFLAGS="$CXXFLAGS -DLIGHTNING_KMP"

Secp256k1 modules:

  • cd modules/decredsecp256k1
    make
    export CXXFLAGS="$CXXFLAGS -DDECRED_SECP256K1"
  • git submodule update --init --recursive external/secp256k1
    cd modules/secp256k1
    make
    export CXXFLAGS="$CXXFLAGS -DSECP256K1"
  • cd modules/nbitcoinsecp256k1
    make
    export CXXFLAGS="$CXXFLAGS -DNBITCOIN_SECP256K1"
  • cd modules/rustk256
    make
    export CXXFLAGS="$CXXFLAGS -DRUST_K256"

Utreexo Modules

  • cd modules/utreexo
    make
    export CXXFLAGS="$CXXFLAGS -DUTREEXO"
  • cd modules/rustreexo
    make
    export CXXFLAGS="$CXXFLAGS -DRUSTREEXO"

Final Build and Execution

Once the modules are compiled, you can compile bitcoinfuzz and execute it:

make
FUZZ=target_name ./bitcoinfuzz

Selective Module Loading

By default, all compiled modules are loaded when running the fuzzer. You can use the MODULES environment variable to load only specific modules at runtime, without needing to recompile.

Usage

Set the MODULES environment variable to a comma-separated list of module names:

MODULES="BITCOIN_CORE,RUST_BITCOIN" FUZZ=target_name ./bitcoinfuzz

Examples

Load only Bitcoin Core and rust-bitcoin for comparison:

MODULES="BITCOIN_CORE,RUST_BITCOIN" FUZZ=bip32_master_keygen ./bitcoinfuzz

Load Lightning implementations only:

MODULES="LDK,LND,CLIGHTNING" FUZZ=deserialize_invoice ./bitcoinfuzz

Load all compiled modules (default behavior):

FUZZ=target_name ./bitcoinfuzz

Notes

  • If MODULES is unset or empty, all compiled modules are loaded (existing behavior)
  • The fuzzer will abort with an error if you request a module that was not compiled
  • Whitespace around module names is trimmed (e.g., "BTCD, LND" works)

Bugs/inconsistences/mismatches found by Bitcoinfuzz

About

Differential Fuzzing of Bitcoin protocol implementations and libraries

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors