Skip to content

ci(fuzz): discover fuzz targets dynamically#1290

Merged
Benoît Cortier (CBenoit) merged 4 commits into
masterfrom
ci/fuzz-new-targets
May 20, 2026
Merged

ci(fuzz): discover fuzz targets dynamically#1290
Benoît Cortier (CBenoit) merged 4 commits into
masterfrom
ci/fuzz-new-targets

Conversation

@CBenoit

Copy link
Copy Markdown
Member

Mirror the check features pattern: expose cargo xtask fuzz list (with --format github-matrix) that enumerates fuzz targets by scanning fuzz/fuzz_targets/*.rs, and have the fuzz workflow build its matrix from that output. New targets dropped into the directory are picked up by CI automatically, with no workflow edits required.

Mirror the `check features` pattern: expose `cargo xtask fuzz list`
(with `--format github-matrix`) that enumerates fuzz targets by
scanning `fuzz/fuzz_targets/*.rs`, and have the fuzz workflow build
its matrix from that output. New targets dropped into the directory
are picked up by CI automatically, with no workflow edits required.

Copilot AI left a comment

Copy link
Copy Markdown

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 updates the xtask fuzz tooling and CI workflow so fuzz targets are discovered dynamically by scanning fuzz/fuzz_targets/*.rs, allowing the GitHub Actions fuzz job matrix to be generated automatically without hardcoding target names in the workflow.

Changes:

  • Removed the hardcoded FUZZ_TARGETS list and replaced it with filesystem-based discovery (discover_targets).
  • Added cargo xtask fuzz list (supports --format human and --format github-matrix) to expose discovered targets for local use and CI.
  • Updated the fuzz GitHub Actions workflow to build its job matrix from cargo xtask fuzz list --format github-matrix.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
xtask/src/prelude.rs Removes re-export of the removed FUZZ_TARGETS constant.
xtask/src/main.rs Drops FUZZ_TARGETS, switches to typed ListFormat, and wires up fuzz list.
xtask/src/fuzz.rs Implements dynamic fuzz target discovery and emits GitHub matrix JSON.
xtask/src/cov.rs Uses dynamic fuzz target discovery when generating fuzz coverage.
xtask/src/cli.rs Adds fuzz list subcommand and introduces ListFormat parsing/defaulting.
.github/workflows/fuzz.yml Adds a setup job to generate the fuzz matrix dynamically and uses matrix.include.

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

Comment thread xtask/src/fuzz.rs Outdated
Comment thread xtask/src/fuzz.rs Outdated
@CBenoit Benoît Cortier (CBenoit) merged commit 5663ce2 into master May 20, 2026
31 of 33 checks passed
@CBenoit Benoît Cortier (CBenoit) deleted the ci/fuzz-new-targets branch May 20, 2026 15:06
Greg Lamberson (glamberson) added a commit to lamco-admin/IronRDP that referenced this pull request May 26, 2026
Closes Devolutions#1124 task 2 ("message_decoding_invariants"). New assertion-based
oracle that complements `pdu_round_trip` (Devolutions#1291, task 1) by asserting
the `Encode::size()` soundness contract on every decoder-accepted input:
`pdu.size() == encode_vec(&pdu).len()`.

The Encode trait's `size()` is the contract caller code relies on for
buffer sizing (`ensure_size!`, `cast_length!`, downstream allocations).
A size that lies about its own length produces under-allocation
(truncated encode) or over-allocation (wasted memory + buffer-overflow
risk depending on surrounding context). The workspace already asserts
this property against known-good byte fixtures in
`ironrdp-testsuite-core/tests/pdu/rdp.rs::buffer_length_is_correct_for_*`;
this oracle extends the assertion across the fuzz input surface.

Distinct from `pdu_round_trip`:

- `pdu_round_trip` silently drops `Err` and catches panics only.
- `message_decoding_invariants` ASSERTS on the size contract, so a
  violation aborts the fuzz iteration as a libFuzzer crash (the bug
  reporting mechanism per the oracle module doc).

What this catches (that `pdu_round_trip` does not):

- `Encode::size()` lies about its own size (returns N but `encode`
  writes M ≠ N), causing buffer over/under-allocation in callers.
- Decode-acceptable bytes that the encoder cannot reconstruct to the
  same length (lossy decode that loses framing structure).

What this does NOT catch (covered by other oracles):

- Decode-time panics or OOM (covered by `pdu_decode`).
- Re-decode equality after round-trip (covered by `pdu_round_trip`'s
  silent-drop pattern; assertion-based re-decode is intentionally out
  of scope here to keep the oracle's failure mode unambiguous).

Type coverage mirrors `pdu_round_trip` exactly. This includes
`capability_sets::CapabilitySet` and `headers::ShareControlHeader`
(the latter transits through `CapabilitySet`'s encoder via the Active
variants). Both currently hit the `BitmapCacheV3` encoder
`unreachable!()` panic at `capability_sets/mod.rs:447` per issue Devolutions#1292.

**Merge dependency: this PR depends on Devolutions#1313** (the fix for Devolutions#1292)
landing first. Until Devolutions#1313 merges, smoke fuzz on this oracle reproduces
Devolutions#1292 in under 1 second on input `[6, 0, 6, 0, 0, 0]` (a longer variant
of Devolutions#1292's original 4-byte reproducer). After Devolutions#1313 lands, the panic is
gone and this oracle covers the full type cohort cleanly.

New target auto-discovers into CI via `cargo xtask fuzz list` per
PR Devolutions#1290's dynamic CI fan-out.

Test plan: `cargo xtask check fmt/lints/tests/typos/locks` all pass;
new `check_message_decoding_invariants` regression-replay test in
`crates/ironrdp-testsuite-core/tests/fuzz_regression.rs` runs and
passes against the seed corpus. Smoke fuzz with the two Devolutions#1313-blocked
types excluded was clean over 6,667,709 iterations in 121 seconds at
~55K exec/s sustained, indicating no additional size-contract
violations beyond the known Devolutions#1313-blocked panic in the covered type
cohort.

Refs Devolutions#1120 (umbrella), closes Devolutions#1124 task 2.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants