#36631 fuzz: add a custom mutator for PSBT
| Reviewability | Reviews | Agreement | Size | ||||
|---|---|---|---|---|---|---|---|
| #36631 fuzz: add a custom mutator for PSBT | Ready | 0 | Blocked | 11 + 559 tests | |||
|
|
|
|
| |||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | |||||||
Goal
Adds framework support for target-specific libFuzzer custom mutators and introduces a structure-aware custom mutator for PSBT deserialization. The harness exposes an optional `mutator` hook in `FuzzTargetOptions` that dynamically dispatches to custom mutators or falls back to `LLVMFuzzerMutate`, preserving default length control behavior for standard targets. The new PSBT mutator parses inputs into key-value records across global, input, and output maps, generating valid cryptographic primitives such as secp256k1 pubkeys, xpubs, and taproot control blocks so the fuzzer can traverse deeper PSBT parser paths.
Problem: The PSBT deserialization fuzz target feeds raw bytes directly into the parser. Because byte-level mutations almost never produce valid key-value container maps or structurally valid cryptographic keydata, inputs are rejected at superficial validation checks and deeper deserialization logic is rarely reached.
Evidence of importance:
- PR description: 'Byte-level mutations rarely produce well-formed key-value maps with valid keydata (pubkeys, xpubs, control blocks, DER signatures, etc), so most inputs are rejected early and the deeper per-field parsing is rarely reached.'
- Commit 825c18f07d notes libFuzzer-specific quirks: 'libFuzzer disables -len_control by default whenever LLVMFuzzerCustomMutator is linked in, which would otherwise affect every target. Targets without a custom mutator get -len_control=100 restored, unless it was set explicitly.'
Scope: Adds a new utility module in `src/test/fuzz/util/psbt.cpp` that contains PSBTv0/v2 template generation and field mutation logic ported from bitcoinfuzz, with author-noted LLM assistance.
Risk of a mistake: low (tests). The changes only affect fuzz testing targets and fuzz build configurations, with no impact on consensus, node operation, or release binaries.
Changed since the previous assessment: Unchanged in approach; continues providing the custom mutator framework hook and the PSBT structure-aware mutator.
Reviewability
Ready
Nothing known that would invalidate a review now.
Waiting on (from the thread read):
- author: fix afl++ breakage in OSS-Fuzz 2026-10-30
Agreement
Blocked
objection that needs a change, nobody has replied (ekzyis)
Thread notes: Updated ekzyis's question rc:4419573290 to accepted after their reply rc:4432455324 confirmed the author's explanation.
Objections, suggestions, and questions:
| Reviewer | Type | Harm, request, or question | Status | Must be | Author replied | Quote |
|---|---|---|---|---|---|---|
| ekzyis (contributor) | objection, correctness | breaks fuzzing with afl++ in OSS-Fuzz due to an assertion failure from argv manipulation | open · author acknowledged and deferred to a fix while in draft | changed | yes | 2026-10-30: 'I think this will break fuzzing with afl++ in OSS-Fuzz. It defines LLVMFuzzerMutate as a symbol here and then crashes here because of argv manipulation.' |
| ekzyis (contributor) | question, correctness | asks whether erasing the key when FuzzDefaultMutate returns 0 is intended for key mutations | accepted · reviewer confirmed author's clarification | answered | yes | 2026-10-30: 'The key gets erased if FuzzDefaultMutate returns 0 (which is the case for LLVMFuzzerMutate in afl++) . Is that expected to happen in this branch for key mutations?' Status from: 2026-10-31: ekzyis: 'Ah yes, that is my understanding as well' |
Participants:
- ekzyis (contributor, objection, 3 statements 2026-10-30 to 2026-10-31): reported that fuzzing with afl++ breaks with an assertion failure, and questioned key erasure behavior
Files
562 lines under test/bench/ci.
- src/test/fuzz/util/psbt.cpp +481/-0
- src/test/fuzz/fuzz.cpp +48/-0
- src/test/fuzz/util/psbt.h +13/-0
- CMakeLists.txt +11/-0
- src/test/fuzz/deserialize.cpp +8/-3
- src/test/fuzz/fuzz.h +5/-0
- src/test/fuzz/util/CMakeLists.txt +4/-0
Card
The short text the ranking pass compares across PRs.
Adds custom mutator support to Bitcoin Core's fuzzing harness along with a structure-aware mutator for the PSBT deserialization target. Raw byte mutations rarely satisfy PSBT container framing or cryptographic encodings like compressed pubkeys and Taproot control blocks, preventing fuzzer exploration beyond shallow parsing rejections. The new mutator decomposes inputs into PSBT maps and synthesizes valid field structures and keys to exercise deep deserialization paths. Changes only affect the fuzzing framework and test binaries.
Data
thread read through 2026-10-31 (7 statements), head 3f6be480ed
labels: Fuzzing · draft
Categories
Test infrastructure
P3 · test coverage (ranking pass: Moved up: a reusable custom-mutator hook is framework capability, not just one more target.)
Adds structure-aware mutation to reach deeper PSBT deserialization code paths that byte-level fuzzing cannot effectively exercise. The PR description notes that 'Byte-level mutations rarely produce well-formed key-value maps with valid keydata... so most inputs are rejected early and the deeper per-field parsing is rarely reached.' While it provides valuable fuzzing coverage and adds general custom mutator support to `FuzzTargetOptions`, it targets PSBT parsing rather than consensus or P2P attack surfaces and is reasonably deferrable.
Membership: Touches the fuzzing test harness in `src/test/fuzz/fuzz.cpp` and adds a custom mutator utility in `src/test/fuzz/util/psbt.cpp`.
Factors: security/stability 1, bug 0, performance 0, user value 0, leverage 1