Skip to content

Add Absolution-based stateful fuzzing framework#1585

Merged
N3ur0sis merged 5 commits into
masterfrom
aro/fuzzing-framework
Jul 7, 2026
Merged

Add Absolution-based stateful fuzzing framework#1585
N3ur0sis merged 5 commits into
masterfrom
aro/fuzzing-framework

Conversation

@N3ur0sis

@N3ur0sis N3ur0sis commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds a coverage-guided, state-aware fuzzing framework for the Secure SDK (and Ledger apps), built on Absolution, plus four small SDK library fixes the framework uncovered.

Stateful Ledger apps are multi-APDU protocols that classic stateless harnesses can't reach. Instead of replaying full APDU chains or emulating the device, Absolution injects global state from a declarative model: the fuzzer input is split into [ prefix | tail ], the prefix restores the app's globals to a target state, and the tail is fed to the real APDU dispatcher. This reaches deep protocol logic at near-stateless speed.

One generic harness drives any app's dispatcher; the SDK mock layer replaces hardware-dependent syscalls, crypto, and NBGL; and scripts/app-campaign.sh runs the full pipeline (build → sync invariants → fuzz → LLVM coverage report). Apps opt in by adding a small fuzzing/ subtree and including cmake/LedgerAppFuzz.cmake; nothing is copied out of the SDK.

Main additions:

  • fuzzing/cmake/LedgerAppFuzz.cmake — public entry point; exposes a secure_sdk target aggregating the SDK libraries, mocks, and framework headers.
  • fuzzing/mock/ — SDK-wide syscall / crypto / OS / NBGL mocks.
  • fuzzing/sdk-fuzz/ — 10 SDK self-fuzz targets (alloc, base58, bip32, APDU parser, lists, NFC NDEF, qrcodegen, TLV…).
  • fuzzing/template/ — scaffold for new apps; fuzzing/docs/ — app contract + campaign workflow.
  • .clusterfuzzlite/ + .github/workflows/clusterfuzzlite.yml — ClusterFuzzLite CI (replaces the previous cflite_cron.yml / cflite_pr.yml).

SDK library fixes found by the framework — each isolated in its own cherry-pickable commit with a "found by fuzzing" note:

  • lib_cxng: signed-int shift UB in reverse_32_bits.
  • lib_cxng: NULL memcpy in cx_sha256_update on empty input.
  • lib_tlv: NULL strnlen / memcpy on an empty TLV value.
  • lib_standard_app: NULL memcpy in send_swap_error_with_buffers.

Changes include

  • Bugfix (non-breaking change that solves an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (change that is not backwards-compatible and/or changes current functionality)
  • Tests
  • Documentation
  • Other (for changes that might not fit in any category)

Additional comments

  • Opt-in, additive tooling: no public SDK API or on-device/firmware change. The only library source changes are the 4 one-line defensive fixes above.
  • The framework supersedes the SDK's previous fuzzing setup — the legacy self-fuzz harnesses and the two cflite_* workflows are replaced by the Absolution-driven targets and a single clusterfuzzlite.yml, covering the same libraries and more.
  • The 4 library fixes are deliberately separate, single-file commits so they can be cherry-picked independently of the (tooling-only) framework commit.

Auto cherry-pick in API_LEVEL

[ ] TARGET_API_LEVEL: API_LEVEL_26

@ledger-wiz-cspm-secret-detection

ledger-wiz-cspm-secret-detection Bot commented Jun 5, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 1 Medium
Software Management Finding Software Management Findings -
Total 1 Medium

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

@codecov-commenter

codecov-commenter commented Jun 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.42857% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.88%. Comparing base (acf454a) to head (338b423).

Files with missing lines Patch % Lines
lib_cxng/src/cx_crc32.c 0.00% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1585   +/-   ##
=======================================
  Coverage   91.87%   91.88%           
=======================================
  Files          39       39           
  Lines        4653     4656    +3     
  Branches      596      598    +2     
=======================================
+ Hits         4275     4278    +3     
  Misses        266      266           
  Partials      112      112           
Flag Coverage Δ
unittests 91.88% <71.42%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread fuzzing/libs/lib_nbgl.cmake Outdated
Comment thread fuzzing/template/harness/fuzz_dispatcher.c Outdated
@N3ur0sis N3ur0sis force-pushed the aro/fuzzing-framework branch from 8f36ba5 to e007cf6 Compare June 17, 2026 08:37
@N3ur0sis N3ur0sis marked this pull request as ready for review June 17, 2026 08:51
@N3ur0sis N3ur0sis requested a review from Copilot June 17, 2026 09:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an Absolution-based, coverage-guided state-aware fuzzing framework for the Ledger Secure SDK and opt-in Ledger apps, and includes four small defensive SDK fixes uncovered by fuzzing (NULL/empty-input handling + UB shift fix). It also replaces the previous ClusterFuzzLite workflow setup with a single reusable workflow.

Changes:

  • Add the new fuzzing framework structure (generic harness, mutators, mock layer, campaign scripts, invariants/layout tooling, sanitizer configs) plus SDK self-fuzz targets under fuzzing/sdk-fuzz/.
  • Replace/retire legacy fuzz harnesses, docs, and scripts in favor of the new pipeline (app-campaign.sh + manifest-driven seed/dictionary handling).
  • Apply four one-line SDK library hardening fixes (TLV, swap error helper, SHA-256 update, CRC32 bit-reverse).

Reviewed changes

Copilot reviewed 95 out of 101 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
lib_tlv/tlv_library.c Guard strnlen/memcpy against empty TLV values.
lib_standard_app/swap_error_code_helpers.c Guard memcpy when buffer count is zero.
lib_cxng/src/cx_sha256.c Guard memcpy when len == 0 in cx_sha256_update.
lib_cxng/src/cx_crc32.c Fix signed-shift UB by using uint32_t shifts.
fuzzing/sdk-fuzz/mock/mocks.h SDK self-fuzz harness globals declarations.
fuzzing/sdk-fuzz/mock/mocks.c SDK self-fuzz harness globals definitions + BSS stub.
fuzzing/sdk-fuzz/mock/mock_check_signature_with_pki.c PKI signature-check stub for TLV fuzzing targets.
fuzzing/sdk-fuzz/macros/exclude_macros.txt SDK self-fuzz macro exclusions.
fuzzing/sdk-fuzz/macros/add_macros.txt SDK self-fuzz extra macros.
fuzzing/sdk-fuzz/invariants/zero-symbols.txt Placeholder list for per-target invariant zeroing policy.
fuzzing/sdk-fuzz/invariants/domain-overrides.txt Placeholder for per-target invariant domain overrides.
fuzzing/sdk-fuzz/harness/fuzz_tlv_trusted_name.c Self-fuzz target for TLV trusted-name use case.
fuzzing/sdk-fuzz/harness/fuzz_tlv_dynamic_descriptor.c Self-fuzz target for TLV dynamic-descriptor use case.
fuzzing/sdk-fuzz/harness/fuzz_qrcodegen.c Self-fuzz target for qrcodegen encode paths.
fuzzing/sdk-fuzz/harness/fuzz_nfc_ndef.c Self-fuzz target for NFC NDEF parsing/formatting.
fuzzing/sdk-fuzz/harness/fuzz_bip32.c Self-fuzz target for BIP32 path read/format round-trips.
fuzzing/sdk-fuzz/harness/fuzz_base58.c Self-fuzz target for base58 encode/decode round-trips.
fuzzing/sdk-fuzz/harness/fuzz_apdu_parser.c Self-fuzz target for APDU parsing correctness invariants.
fuzzing/sdk-fuzz/harness/fuzz_alloc.c Self-fuzz target for allocator operations stream.
fuzzing/sdk-fuzz/harness/fuzz_alloc_utils.c Self-fuzz target for app_mem_utils allocator helpers.
fuzzing/sdk-fuzz/fuzz-manifest.toml Multi-target manifest describing SDK self-fuzz targets and dictionaries.
fuzzing/sdk-fuzz/CMakeLists.txt Build Absolution-based SDK self-fuzz targets.
fuzzing/scripts/update-scenario-layout.py Extract Absolution prefix/control offsets into scenario_layout.h.
fuzzing/scripts/tune-invariant-domains.py Apply post-sync domain overrides to .zon invariants.
fuzzing/scripts/sync-invariant.py Sync Absolution-discovered invariants + apply zero-symbol policy.
fuzzing/scripts/generate-seeds.py Manifest-driven seed generation orchestrator.
fuzzing/scripts/generate-seed-corpus-generic.py Generic seed corpus generator (CLA/INS-driven).
fuzzing/scripts/fuzz_seed_utils.py Shared helpers for resolving prefix/layout and generating ctrl bytes.
fuzzing/scripts/fuzz_manifest.py TOML manifest reader/validator + dictionary/compat key helpers.
fuzzing/scripts/app-config.sh Read manifest and export campaign pipeline variables.
fuzzing/scripts/app-common.sh Shared campaign helpers (configure/build/sync/layout/merge/etc.).
fuzzing/sanitizers/ubsan-suppressions.txt Runtime UBSan suppressions placeholder.
fuzzing/sanitizers/ubsan-options.env UBSan runtime options for campaigns.
fuzzing/sanitizers/ubsan-ignorelist.txt Compile-time sanitizer ignorelist (special-case list).
fuzzing/sanitizers/load-options.sh Load sanitizer runtime env options from .env files.
fuzzing/sanitizers/asan-options.env ASan runtime options for campaigns.
fuzzing/README.md Rewrite docs to match the new framework & workflow.
fuzzing/mock/tlv_mutator.c TLV grammar-aware custom mutator implementation.
fuzzing/mock/README.md Document SDK fuzz mock layer layout and conventions.
fuzzing/mock/os/pic.c PIC translation mock implementations.
fuzzing/mock/os/os_runtime.c Host-side OS/libc runtime mocks (strlcpy/strlcat, etc.).
fuzzing/mock/os/os_exceptions.c Exception/NVM mocks; os_sched_exit longjmps to harness.
fuzzing/mock/mock.cmake Build the mock library + syscall stub generation.
fuzzing/mock/cx/cx_crypto.c Crypto/perso mocks + fuzz-controlled failure injection.
fuzzing/mock/cx/cx_bn_ec.c Big-number and EC-point mock implementations.
fuzzing/mock/custom/pic.c Remove legacy custom mock (superseded by new mock layer).
fuzzing/mock/custom/os_task.c Remove legacy custom mock (superseded by new mock layer).
fuzzing/mock/custom/mocks.c Remove legacy custom mocks (superseded by new mock layer).
fuzzing/mock/custom/main.c Remove legacy custom mock entrypoint bits.
fuzzing/macros/macros.cmake Improve macro extraction/override logic for app vs SDK builds.
fuzzing/local_run.sh Remove legacy local-run wrapper script (superseded by campaign).
fuzzing/libs/lib_tlv.cmake Update TLV lib build rules for new framework graph.
fuzzing/libs/lib_standard_app.cmake Update standard_app lib build rules for new framework graph.
fuzzing/libs/lib_qrcode.cmake Update qrcode lib build rules for new framework graph.
fuzzing/libs/lib_pki.cmake Update PKI lib build rules for new framework graph.
fuzzing/libs/lib_nfc.cmake Update NFC lib build rules for new framework graph.
fuzzing/libs/lib_nbgl.cmake Update NBGL lib build rules; exclude nbgl_use_case sources.
fuzzing/libs/lib_lists.cmake Update lists lib build rules for new framework graph.
fuzzing/libs/lib_io.cmake Update IO lib build rules for new framework graph.
fuzzing/libs/lib_glyphs.cmake Update glyph generation rules for new framework graph.
fuzzing/libs/lib_cxng.cmake Update cxng lib build rules; now includes mock module.
fuzzing/libs/lib_alloc.cmake Update alloc lib build rules for new framework graph.
fuzzing/invariants/sdk-zero-symbols.txt Introduce SDK/NBGL global zero-symbol policy.
fuzzing/include/tlv_mutator.h Public TLV mutator interface + optional dispatch helper.
fuzzing/include/fuzz_mutator.h Prefix-aware custom mutator helper for Absolution split inputs.
fuzzing/include/fuzz_layout_check.h Compile-time layout consistency checks.
fuzzing/include/fuzz_harness.h Generic Absolution APDU harness entry helper.
fuzzing/include/fuzz_defs.h Shared constants and command spec type for fuzz harnesses.
fuzzing/harness/fuzzer_qrcodegen.c Remove legacy SDK harness (replaced by sdk-fuzz targets).
fuzzing/harness/fuzzer_bip32.c Remove legacy SDK harness (replaced by sdk-fuzz targets).
fuzzing/harness/fuzzer_base58.c Remove legacy SDK harness (replaced by sdk-fuzz targets).
fuzzing/harness/fuzzer_apdu_parser.c Remove legacy SDK harness (replaced by sdk-fuzz targets).
fuzzing/harness/fuzzer_alloc.c Remove legacy SDK harness (replaced by sdk-fuzz targets).
fuzzing/harness/fuzzer_alloc_utils.c Remove legacy SDK harness (replaced by sdk-fuzz targets).
fuzzing/extra/qrcodegen.cmake Remove legacy per-fuzzer cmake (replaced by sdk-fuzz CMakeLists).
fuzzing/extra/lib_lists.cmake Remove legacy per-fuzzer cmake (replaced by sdk-fuzz CMakeLists).
fuzzing/extra/lib_alloc.cmake Remove legacy per-fuzzer cmake (replaced by sdk-fuzz CMakeLists).
fuzzing/extra/bip32.cmake Remove legacy per-fuzzer cmake (replaced by sdk-fuzz CMakeLists).
fuzzing/extra/base58.cmake Remove legacy per-fuzzer cmake (replaced by sdk-fuzz CMakeLists).
fuzzing/extra/apdu_parser.cmake Remove legacy per-fuzzer cmake (replaced by sdk-fuzz CMakeLists).
fuzzing/docs/fuzz_lists.md Remove legacy per-fuzzer doc (superseded by new docs).
fuzzing/docs/CAMPAIGN_WORKFLOW.md Add detailed campaign workflow reference.
fuzzing/CMakeLists.txt Rework fuzzing build graph + sanitizer flags + secure_sdk target.
fuzzing/cmake/LedgerAppFuzz.cmake Add public app entrypoint module + Absolution fetching/wrappers.
.gitignore Ignore generated fuzz artifacts, sdk-fuzz invariants/layout, coverage outputs.
.github/workflows/clusterfuzzlite.yml Replace legacy CFL workflows with one reusable workflow.
.github/workflows/cflite_pr.yml Remove legacy CFL PR workflow.
.github/workflows/cflite_cron.yml Remove legacy CFL cron workflow.
.clusterfuzzlite/Dockerfile Update CFL Dockerfile to match new sdk-fuzz build flow.
.clusterfuzzlite/build.sh Build Absolution-based sdk-fuzz targets and export fuzzers to $OUT.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread fuzzing/include/fuzz_harness.h
Comment thread fuzzing/scripts/fuzz_manifest.py
Comment thread fuzzing/libs/lib_cxng.cmake
Comment thread fuzzing/mock/mock.cmake
@bboilot-ledger bboilot-ledger requested review from a team and tdejoigny-ledger and removed request for a team June 17, 2026 11:42
@N3ur0sis N3ur0sis force-pushed the aro/fuzzing-framework branch 2 times, most recently from 9b675fa to 7e7650e Compare June 29, 2026 11:41

@iartemov-ledger iartemov-ledger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just few comments.
For me it's almost ready, and for you? Do you plan any new changes?
In order to understand if it can be easily extended I've coded with Claude the Fuzzing framework nbgl page addon (can be merged either into this PR or later directly to master or refused if incorrect).

Also there are few Copilot warnings and a rebase to do.

Comment thread .clusterfuzzlite/Dockerfile
Comment thread fuzzing/README.md
Comment thread fuzzing/mock/mock.cmake
Comment thread fuzzing/sdk-fuzz/fuzz-manifest.toml
@N3ur0sis N3ur0sis force-pushed the aro/fuzzing-framework branch 3 times, most recently from 49b4056 to 7813e8c Compare July 6, 2026 11:56

@iartemov-ledger iartemov-ledger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool work!

@N3ur0sis N3ur0sis force-pushed the aro/fuzzing-framework branch from 7813e8c to 27b7984 Compare July 7, 2026 09:03
N3ur0sis added 5 commits July 7, 2026 11:05
Found by SDK fuzzing (UBSan): 1 << i is undefined for a 32-bit signed int
at i == 31. Shift as uint32_t instead.
Found by SDK fuzzing (UBSan): skip the copy when len == 0 so a NULL data
pointer is never passed to memcpy.
Found by SDK fuzzing (UBSan): guard the scan and copy on value.size > 0 so
a NULL value pointer is never passed to strnlen/memcpy.
Found by SDK fuzzing (UBSan): skip the buffer copy when count == 0 so a
NULL pointer is never passed to memcpy.
@N3ur0sis N3ur0sis force-pushed the aro/fuzzing-framework branch from 27b7984 to 338b423 Compare July 7, 2026 09:06
@N3ur0sis N3ur0sis merged commit c74d511 into master Jul 7, 2026
235 checks passed
@N3ur0sis N3ur0sis deleted the aro/fuzzing-framework branch July 7, 2026 09:21
@bboilot-ledger bboilot-ledger restored the aro/fuzzing-framework branch July 7, 2026 09:37
@bboilot-ledger bboilot-ledger deleted the aro/fuzzing-framework branch July 7, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants