Skip to content

Add advanced fuzz targets to verify more invariants (round-trip, framing, state-machine…) #1124

@CBenoit

Description

Move beyond “bytes-only decode fuzzing” to higher-signal fuzzing that asserts invariants and exercises state machines.

Proposed fuzz targets

  1. round_trip (typed):
  • Generate a structured PDU (Arbitrary)
  • Encode -> Decode -> Compare (plus size invariants)
  1. message_decoding_invariants (bytes):
  • Feed raw bytes into decoder(s)
  • If decoding succeeds, assert framing/size/consumption invariants
  1. connector_state_machine (optional in this issue; can be a follow-up):
  • Generate sequences / frames and step connector state machines
  • Validate buffer bookkeeping and hint-based framing invariants

Tasks

  • Add round_trip fuzz target wired to an oracle module
  • Add at least one non-trivial oracle invariant (not just “no panic”)
  • Ensure targets can be run via cargo xtask fuzz run <target>
  • Add regression test mechanism: replay minimized crashers in unit tests

“Round-trip” oracle (typed fuzzing)

This is a high signal harness: generate a structured PDU, encode it, decode it back, compare.

Fuzz target (tiny):

#![no_main]
use libfuzzer_sys::fuzz_target;

use ironrdp_fuzzing::{generators::AnyPdu, oracles};

fuzz_target!(|msg: AnyPdu<'_>| {
    oracles::round_trip(msg);
});

Generator idea (AnyPdu<'a>):

  • an enum that contains a curated set of PDUs we want to round-trip (start small; expand over time).

  • it should provide:

    • encode() (to Vec<u8>)
    • decode_owned(bytes) (back into an owned representation)
    • into_owned() for comparison

Oracle idea:

  • encode
  • decode_owned
  • assert_eq!(decoded, original.into_owned())
  • also assert size invariants (encoded.len() == original.size()) where applicable

This is the harness that typically finds:

  • wrong length computations
  • missing fields in encode/decode
  • decode accepting invalid encodings (or encode producing something decode can’t parse)

“Message decoding” oracle (raw bytes, but with invariants)

We keep a raw-bytes decoder too, but it should assert invariants, e.g.:

  • if decoding succeeds, then “frame-size detection” should succeed and be consistent
  • if decode consumes N bytes, that should match computed/expected sizes
  • avoid silent under/over-reads

This is especially valuable for:

  • frame boundary logic
  • “find size” / “hint” logic
  • partial frame handling

State-machine fuzzing (connector sequences)

We also want fuzzing that targets state machines (connector / acceptor / channel sequences), not just stateless PDUs.

The typical pattern:

  • generate a connector + an input payload

  • capture the connector’s “expected next PDU hint”

  • call step(payload, out_buf)

  • if it succeeds:

    • verify output length bookkeeping is correct (buffer length matches returned “written length”)
    • verify that the hint can indeed determine a frame length for payload (since the step succeeded)

We can start with “single-step” fuzzing and later move to “multi-step” inputs (a sequence of frames) to reach deeper states.

Acceptance criteria

  • Oracles assert invariants (round-trip equality and/or framing invariants)
  • Running the fuzz target locally is documented

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-coreArea: Core tierfuzzingCrashs and other bugs found by fuzzing

    Type

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions