Dev.to Security 🔐 Cybersecurity 👁 0 📖 2 min read

What auditing a Bitcoin 0.1.0 fork in 2026 turned up

Bitflash started from the source of Bitcoin 0.1.0, the 2009 release, ported to a modern toolchain. Two parts were replaced: proof of work is RandomX, and IP addresses are gone, so every node is a Tor onion service. Befor

Bitflash started from the source of Bitcoin 0.1.0, the 2009 release, ported to a modern toolchain. Two parts were replaced: proof of work is RandomX, and IP addresses are gone, so every node is a Tor onion service. Before release 1.2.33 we audited the whole node and published every finding. Here is what that looked like and what it found.

The tooling

Everything free and open:

  • Static analysis: CodeQL, cppcheck, clang-tidy, the Clang Static Analyzer, flawfinder and Semgrep.
  • Fuzzing: 19 harnesses, each run under libFuzzer, AFL++ and honggfuzz with AddressSanitizer and UndefinedBehaviorSanitizer, sharing one corpus per target.
  • Instrumented self-tests: every self-test under ASan, under ThreadSanitizer and under Valgrind.
  • Reproducibility: reprotest builds the node twice, varying time, locale, umask and paths, and compares the binaries.

The whole pass is one script, scripts/audit/run-audit.sh, so anyone can rerun it.

Things worth sharing

A checksum hid the P2P layer from the fuzzer. Every P2P message carries a double-SHA256 checksum of its payload. A fuzzer feeding raw bytes almost never forges it, so the first network harness had 89% coverage of the envelope and never reached block or transaction handling. A second harness hands payloads straight to the message handler after a simulated handshake. That one found real bugs: a leak of every duplicate block, and a pool that freed refused blocks twice.

Windows and Linux disagreed on consensus. A script number of 2^32 or more read as 0 on Windows (where long is 32 bits) and as INT_MAX on Linux. OP_PICK, OP_ROLL and OP_CHECKMULTISIG used it. A block carrying such a script would be valid on one OS and invalid on the other, splitting the network. We scanned all 225,066 mainnet scripts: none used such a number, so the fix needed no activation height.

A 2009 log buffer could deadlock a node. When a log line did not fit, the NUL ended up before the newline, the buffer never drained, and the next vsnprintf wrote over the mutex right after it. Every thread that logged then blocked. We found it with gdb on a stuck pool, by relinking the unstripped objects to get symbols for the same build.

711 leak reports, two real bugs. The network fuzzer reported 711 leaks. A single-process run of 5.5 hours, with full stacks, showed they were Berkeley DB's own region blocks, allocated once when the database environment opens. Only two leaks the audit found were real, and both are fixed with tests.

The result

On the release commit: no crash in any fuzz target, 101 self-tests clean under all three instruments, self-test line coverage up from 55% to 66.5%, and a bit-for-bit reproducible build. What we deferred, and why, is written down too.

This is a maintainers' audit, not a third-party firm's. The point of publishing it all is that you don't have to trust it: you can check it.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.