|
Embedded SDK
Embedded SDK
|
fuzz-manifest.toml is the declarative description of what to fuzz. The campaign and CI read it to generate seeds, build a dictionary, compute the corpus compatibility key, and drive the coverage report. It comes in two shapes; the tooling detects which one is in use.
Most apps ship one fuzzer and use a [target] table:
[target]
fuzzer = "fuzz_globals" # target name; also the binary name
harness_version = "1" # bump to invalidate old corpora (see below)
[coverage]
key_files = ["src/handler/get_public_key.c"] # files to report coverage for
[seeds]
ins = [0x01, 0x02, 0x03] # one seed per entry; see [seeds] below
[dictionary]
tokens = [
{ name = "p2_first", value = "\\x00" },
{ name = "magic", value = "BTC" },
]
Required: [target].fuzzer, [target].harness_version, [coverage].key_files, [seeds].ins. Everything else is optional.
Trees with several fuzzers (the SDK self-fuzz suite, or an app that fuzzes several libraries) use a [sdk] table plus one [[targets]] entry per binary:
[sdk]
harness_version = "2" # inherited by every target unless overridden
[coverage]
exclude_regexes = [ # drop framework/harness files from the report
'.*fuzzing/mock/.*',
'.*fuzz_dispatcher\.c',
]
[[targets]]
fuzzer = "fuzz_base58"
key_files = ["lib_standard_app/base58.c"]
seeds = { ins = [0x01, 0x02, 0x03] }
[targets.dictionary]
tokens = [
{ name = "b58_addr", value = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" },
]
[[targets]]
fuzzer = "fuzz_bip32"
key_files = ["lib_standard_app/bip32.c"]
seeds = { ins = [0x01] }
Each [[targets]] entry inherits harness_version from [sdk] (and may override it), and [coverage].exclude_regexes is shared by all targets. A campaign runs every target, or a --target-filtered subset, and produces one combined coverage report.
[targets.dictionary] is TOML's syntax for a sub-table of the last [[targets]] entry. Keep it directly under the entry it belongs to.| Key | Required | Meaning |
|---|---|---|
fuzzer | yes | Target name, and the name of the built binary |
harness_version | yes | Opaque version string in the compatibility key |
In a multi-target manifest, harness_version lives in [sdk] (or at the top level) and each [[targets]] entry provides its own fuzzer.
| Key | Required | Meaning |
|---|---|---|
key_files | yes (single) | Files the key-files-coverage.txt summary focuses on |
exclude_regexes | no | Regexes of paths to drop from the report (mock/harness noise) |
In a multi-target manifest key_files is set per [[targets]] entry.
The generic seed generator turns these into starter APDUs.
| Key | Required | Meaning |
|---|---|---|
ins | yes | One seed per entry (see the note below) |
generic.enabled | no (default true) | Emit generic APDU seeds |
custom.enabled | no (default false) | Also run a custom generator (see below) |
custom.script | when custom enabled | Path to a script.py <out_dir> seed generator |
ins is a count, not a list of instruction bytes. The generator emits one seed per entry and writes the entry's index into control byte 1, which the harness then reduces modulo the app's command count (see The harness). The byte values only appear in seed filenames. Keep the list the same length as fuzz_commands[]: shorter and the trailing commands get no seeds at all, longer and seeds duplicate. CLA and INS themselves never come from the input – they come from the selected command spec – which is why there is no cla key.A custom generator is resolved relative to the fuzzing folder, then APP_DIR, then the SDK scripts directory:
[seeds] ins = [0x01] [seeds.custom] enabled = true script = "scripts/generate-seeds-eth.py"
tokens become a LibFuzzer dictionary that biases mutation toward meaningful byte sequences (tags, magic strings, enum values). Each token is { name = "...", value = "..." }. Use \\xNN in the TOML value to emit a \xNN byte in the dictionary:
[dictionary]
tokens = [
{ name = "tag_version", value = "\\x02" },
{ name = "ticker", value = "USD" },
]
harness_version is one input to the corpus compatibility key:
sha256( prefix_size || sha256(fuzz_globals.zon) || fuzzer || harness_version )
A promoted base corpus is only reused when its key matches the current build. Bump harness_version whenever a change makes older corpora meaningless — for example when you change how the tail is parsed into an APDU, or change the command table in a way that shifts what an input means. Sanitizer, SDK, and toolchain changes are already captured by the other key components, so they do not need a manual bump.