|
Embedded SDK
Embedded SDK
|
Fuzzing feeds an application a stream of automatically generated, mostly invalid inputs and watches for crashes, memory errors, and undefined behaviour. It is one of the cheapest ways to find the parsing and state-handling bugs that matter most on a signing device, because it explores inputs no hand-written test would think to try.
This framework runs a Ledger app on a Linux host and fuzzes it with LibFuzzer under Clang's address / undefined-behaviour / memory sanitizers. Each fuzz target is a single self-contained binary that drives one APDU (or one library entry point) through the same code the device runs. When a target hits a bug it stops, prints a report, and writes the offending input to a crash-* file so it can be replayed.
The framework lives entirely in the SDK, under ${BOLOS_SDK}/fuzzing/. An app opts in by adding a small fuzzing/ folder and including one CMake module; nothing is copied out of the SDK. Its primary home is continuous integration: every pull request fuzzes the changed code so regressions are caught before they reach master and a release (see CI and maintainability).
app-campaign.sh script builds the target, generates starter inputs, runs a short warmup phase (find coverage fast) followed by a longer main phase (dig deeper), then replays the surviving inputs against a coverage build to produce an HTML report. See Running a campaign.harness_version). Tooling refuses to reuse a base corpus whose key does not match the current build, which prevents feeding stale inputs to a changed binary.Every fuzzer input is two regions:
[ prefix | tail ]
P1, P2) followed by the APDU payload. CLA and INS come from the selected command spec rather than the input, so the fuzzer never has to guess a valid pair. See The harness for the byte-by-byte layout.The split point (prefix size) is recomputed after every build and passed to the build as -DFUZZ_PREFIX_SIZE; it is the only layout fact anything outside Absolution knows, and nothing needs the offset of an individual global.
The first bytes of the harness input steer the harness rather than feeding the app:
FUZZ_STRUCTURED_LANE_THRESHOLD (102) the harness takes the structured lane; at or below it the raw lane.Because these are input bytes rather than restored globals, the mutator can flip lane or command directly — see Mutators.
Structured lane** — the harness selects a command from the table, clamps P1/P2 to their declared maxima, and builds a syntactically correct APDU. It drives the happy path and multi-step flows efficiently.
Raw lane** — P1/P2 are passed through without clamping and the payload is whatever the fuzzer produced. It exercises parsing edges and error paths that well-formed inputs never reach.
The threshold 102 divides the 0–255 range roughly 40 / 60 (raw / structured) so each lane gets a meaningful share of the corpus automatically.
Most apps need only one command table.** Define a single fuzz_commands[] covering all your APDUs and both lanes will exercise every command. You only need separate tables — and must override FUZZ_PICK_COMMAND_STRUCTURED / FUZZ_PICK_COMMAND_RAW — when two entry paths are structurally different enough to warrant it (for example a normal APDU handler and a swap callback that share no commands). See The harness for the override macros.
LibFuzzer
|-> (Absolution-generated entry) restore globals from the prefix
|-> fuzz_entry app, in harness/fuzz_dispatcher.c
|-> fuzz_harness_entry SDK, fuzzing/include/fuzz_harness.h
|- pick a command from fuzz_commands[]
|- build one APDU from the tail
|-> fuzz_app_dispatch app adapter
|-> the real app dispatcher (device code)
Because state that would normally accumulate over several messages is restored directly into globals, one APDU per iteration is enough for coverage-guided fuzzing.
| Page | What it covers |
|---|---|
| Running a campaign | Run a campaign, read its output, tune duration and workers |
| Integrating an app | Add fuzzing to an app: the fuzzing/ tree and each file in it |
| Corpus and compatibility keys | Promote, reuse, and version a corpus |
| CI and maintainability | How fuzzing runs in CI and how to keep it healthy |
| Reference | Headers, scripts, environment variables, and layout |