Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Structured fuzzers

The structured fuzzer suite models a curl transfer as a protobuf Scenario. libprotobuf-mutator mutates the message rather than an unstructured byte stream, then a target-specific policy normalizes it before the harness runs curl against a bounded local peer.

The targets still consume binary files. The textproto files under scenarios/curl_fuzzer_proto/ are readable seed sources; CMake encodes them as binary protobuf corpus entries under build/generated_corpora/.

How an input reaches curl

flowchart TD
    subgraph build["Build time"]
        sources["Checked-in schema<br/>and selected curl.h"]
        generator["Validate and stage the schema;<br/>generate the C++ option manifest"]
        schema["build/schemas/curl_fuzzer.proto"]
        messages["Generated C++ Scenario type"]
        manifest["C++ option dispatch manifest"]
        seeds["Checked-in .textproto seeds"]
        encoder["protoc --encode"]
        corpus["Per-target .scenario corpus"]
        target["Structured fuzzer executable<br/>bound to a TargetProfile"]

        sources --> generator
        generator --> schema
        generator --> manifest
        schema --> messages
        schema --> encoder
        seeds --> encoder --> corpus
        messages --> target
        manifest --> target
    end

    subgraph runtime["Each fuzz or replay iteration"]
        input["Binary Scenario input"]
        lpm["libprotobuf-mutator<br/>decode; mutate and cross over when fuzzing"]
        profile{"Fixed-profile target?"}
        normalize["Normalize, prune, and bound"]
        compatibility["Compatibility behavior"]
        runner["Scenario runner"]
        curl["curl"]
        peer["Harness-owned local peer"]

        input --> lpm --> profile
        profile -->|yes| normalize --> runner
        profile -->|curl_fuzzer_proto| compatibility --> runner
        runner --> curl
        runner --> peer
        curl <-->|bounded protocol exchange| peer
    end

    corpus --> input
    target --> input

The CurlOptionId values are checked in because they are part of the serialized corpus format. During the build, the option-manifest generator reads the active options between the schema’s CURL-OPTIONS markers, checks their values against the selected curl checkout’s curl.h, stages the schema under build/schemas/, and generates the C++ dispatch manifest. The remaining message types describe request data, peer responses, and focused API-lifecycle work.

Each thin entrypoint binds the shared runtime to one TargetProfile. Fixed profiles select a protocol, remove fields and options that their peer cannot use, and cap repeated fields and byte budgets. This prevents mutations from spending most of an iteration on inert or unbounded data. The original curl_fuzzer_proto target is the exception: it preserves its historical mixed semantics and corpus without registering a profile postprocessor.

The peers are owned by the harness. Stream protocols generally use local socket pairs; TFTP and HTTP/3 use private loopback UDP endpoints. Baseline curl configuration disables ambient proxies, restricts protocols, redirects direct connections to the harness, and uses short timeouts. A new target or option must preserve those isolation guarantees.

Build and replay a scenario

Build one structured target and its generated seed corpus:

./mainline.sh -t curl_fuzzer_proto_http

Replay one seed with the standalone runner:

./build/curl_fuzzer_proto_http \
  build/generated_corpora/curl_fuzzer_proto_http/basic_get.scenario

The standalone executable accepts files and directories, so an entire target-specific seed set can be replayed too:

./build/curl_fuzzer_proto_http \
  build/generated_corpora/curl_fuzzer_proto_http/

Set FUZZ_VERBOSE=1 when a reproduction needs curl’s protocol trace. These locally built executables replay inputs; OSS-Fuzz builds provide the active libFuzzer mutation engine.

Continue with Writing and inspecting scenarios, then consult Target profiles before choosing a corpus or adding a field.