Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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#!/usr/bin/env python32# Copyright (c) 2019-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Run fuzz test targets.6"""8from concurrent.futures import ThreadPoolExecutor, as_completed9from pathlib import Path10import argparse11import configparser12import logging13import os14import random15import subprocess16import sys19def get_fuzz_env(*, target, source_dir):20symbolizer = os.environ.get('LLVM_SYMBOLIZER_PATH', "/usr/bin/llvm-symbolizer")21fuzz_env = os.environ | {22'FUZZ': target,23'UBSAN_OPTIONS':24f'suppressions={source_dir}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1',25'UBSAN_SYMBOLIZER_PATH': symbolizer,26"ASAN_OPTIONS": "detect_leaks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1",27'ASAN_SYMBOLIZER_PATH': symbolizer,28'MSAN_SYMBOLIZER_PATH': symbolizer,29}30return fuzz_env33def main():34parser = argparse.ArgumentParser(35formatter_class=argparse.ArgumentDefaultsHelpFormatter,36description='''Run the fuzz targets with all inputs from the corpus_dir once.''',37)38parser.add_argument(39"-l",40"--loglevel",41dest="loglevel",42default="INFO",43help="log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console.",44)45parser.add_argument(46'--valgrind',47action='store_true',48help='If true, run fuzzing binaries under the valgrind memory error detector',49)50parser.add_argument(51"--empty_min_time",52type=int,53help="If set, run at least this long, if the existing fuzz inputs directory is empty.",54)55parser.add_argument(56'-x',57'--exclude',58help="A comma-separated list of targets to exclude",59)60parser.add_argument(61'--par',62'-j',63type=int,64default=4,65help='How many targets to merge or execute in parallel.',66)67parser.add_argument(68'corpus_dir',69help='The corpus to run on (must contain subfolders for each fuzz target).',70)71parser.add_argument(72'target',73nargs='*',74help='The target(s) to run. Default is to run all targets.',75)76parser.add_argument(77'--m_dir',78action="append",79help="Merge inputs from these directories into the corpus_dir.",80)81parser.add_argument(82'-g',83'--generate',84action='store_true',85help='Create new corpus (or extend the existing ones) by running'86' the given targets for a finite number of times. Outputs them to'87' the passed corpus_dir.'88)90args = parser.parse_args()91args.corpus_dir = Path(args.corpus_dir)93# Set up logging94logging.basicConfig(95format='%(message)s',96level=int(args.loglevel) if args.loglevel.isdigit() else args.loglevel.upper(),97)99# Read config generated by configure.100config = configparser.ConfigParser()101configfile = os.path.abspath(os.path.dirname(__file__)) + "/../config.ini"102config.read_file(open(configfile))104if not config.getboolean("components", "ENABLE_FUZZ_BINARY"):105logging.error("Must have fuzz executable built")106sys.exit(1)108fuzz_bin=os.getenv("BITCOINFUZZ", default=os.path.join(config["environment"]["BUILDDIR"], 'bin', 'fuzz'))110# Build list of tests111test_list_all = parse_test_list(112fuzz_bin=fuzz_bin,113source_dir=config['environment']['SRCDIR'],114)116if not test_list_all:117logging.error("No fuzz targets found")118sys.exit(1)120logging.debug("{} fuzz target(s) found: {}".format(len(test_list_all), " ".join(sorted(test_list_all))))122args.target = args.target or test_list_all # By default run all123test_list_error = list(set(args.target).difference(set(test_list_all)))124if test_list_error:125logging.error("Unknown fuzz targets selected: {}".format(test_list_error))126test_list_selection = list(set(test_list_all).intersection(set(args.target)))127if not test_list_selection:128logging.error("No fuzz targets selected")129if args.exclude:130for excluded_target in args.exclude.split(","):131if excluded_target not in test_list_selection:132logging.error("Target \"{}\" not found in current target list.".format(excluded_target))133continue134test_list_selection.remove(excluded_target)135test_list_selection.sort()137logging.info("{} of {} detected fuzz target(s) selected: {}".format(len(test_list_selection), len(test_list_all), " ".join(test_list_selection)))139if not args.generate:140test_list_missing_corpus = []141for t in test_list_selection:142corpus_path = os.path.join(args.corpus_dir, t)143if not os.path.exists(corpus_path) or len(os.listdir(corpus_path)) == 0:144test_list_missing_corpus.append(t)145test_list_missing_corpus.sort()146if test_list_missing_corpus:147logging.info(148"Fuzzing harnesses lacking a corpus: {}".format(149" ".join(test_list_missing_corpus)150)151)152logging.info("Please consider adding a fuzz corpus at https://github.com/bitcoin-core/qa-assets")154print("Check if using libFuzzer ... ", end='')155help_output = subprocess.run(156args=[157fuzz_bin,158'-help=1',159],160env=get_fuzz_env(target=test_list_selection[0], source_dir=config['environment']['SRCDIR']),161check=False,162stderr=subprocess.PIPE,163text=True,164).stderr165using_libfuzzer = "libFuzzer" in help_output166print(using_libfuzzer)167if (args.generate or args.m_dir) and not using_libfuzzer:168logging.error("Must be built with libFuzzer")169sys.exit(1)171with ThreadPoolExecutor(max_workers=args.par) as fuzz_pool:172if args.generate:173return generate_corpus(174fuzz_pool=fuzz_pool,175src_dir=config['environment']['SRCDIR'],176fuzz_bin=fuzz_bin,177corpus_dir=args.corpus_dir,178targets=test_list_selection,179)181if args.m_dir:182merge_inputs(183fuzz_pool=fuzz_pool,184corpus=args.corpus_dir,185test_list=test_list_selection,186src_dir=config['environment']['SRCDIR'],187fuzz_bin=fuzz_bin,188merge_dirs=[Path(m_dir) for m_dir in args.m_dir],189)190return192run_once(193fuzz_pool=fuzz_pool,194corpus=args.corpus_dir,195test_list=test_list_selection,196src_dir=config['environment']['SRCDIR'],197fuzz_bin=fuzz_bin,198using_libfuzzer=using_libfuzzer,199use_valgrind=args.valgrind,200empty_min_time=args.empty_min_time,201)204def transform_process_message_target(targets, src_dir):205"""Add a target per process message, and also keep ("process_message", {}) to allow for206cross-pollination, or unlimited search"""208p2p_msg_target = "process_message"209if (p2p_msg_target, {}) in targets:210lines = subprocess.run(211["git", "grep", "--function-context", "ALL_NET_MESSAGE_TYPES{", "src/protocol.h"],212check=True,213stdout=subprocess.PIPE,214text=True,215cwd=src_dir,216).stdout.splitlines()217lines = [l.split("::", 1)[1].split(",")[0].lower() for l in lines if l.startswith("src/protocol.h- NetMsgType::")]218assert len(lines)219targets += [(p2p_msg_target, {"LIMIT_TO_MESSAGE_TYPE": m}) for m in lines]220return targets223def transform_rpc_target(targets, src_dir):224"""Add a target per RPC command, and also keep ("rpc", {}) to allow for cross-pollination,225or unlimited search"""227rpc_target = "rpc"228if (rpc_target, {}) in targets:229lines = subprocess.run(230["git", "grep", "--function-context", "RPC_COMMANDS_SAFE_FOR_FUZZING{", "src/test/fuzz/rpc.cpp"],231check=True,232stdout=subprocess.PIPE,233text=True,234cwd=src_dir,235).stdout.splitlines()236lines = [l.split("\"", 1)[1].split("\"")[0] for l in lines if l.startswith("src/test/fuzz/rpc.cpp- \"")]237assert len(lines)238targets += [(rpc_target, {"LIMIT_TO_RPC_COMMAND": r}) for r in lines]239return targets242def generate_corpus(*, fuzz_pool, src_dir, fuzz_bin, corpus_dir, targets):243"""Generates new corpus.245Run {targets} without input, and outputs the generated corpus to246{corpus_dir}.247"""248logging.info("Generating corpus to {}".format(corpus_dir))249targets = [(t, {}) for t in targets] # expand to add dictionary for target-specific env variables250targets = transform_process_message_target(targets, Path(src_dir))
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.