Fuzz targets, seed corpora, and developer tooling for curl and libcurl. This repository supplies the harnesses used by curl's OSS-Fuzz project.
Documentation · Corpus decoder · Reproducing findings · Benchmarking · Profiling
| Family | Purpose | Seed source |
|---|---|---|
Legacy curl_fuzzer and protocol variants |
Protocol-specific libcurl transfers driven by the established TLV format | corpora/<target>/ |
Structured curl_fuzzer_proto* |
Protobuf scenarios with target-specific policies and in-process protocol peers | scenarios/curl_fuzzer_proto/ |
Direct fuzz_* targets |
Focused fuzzing of URL, buffer queue, DoH, and netrc parsing | corpora/<target>/ |
The conditional list in scripts/fuzz_targets is the
source of truth for targets packaged for OSS-Fuzz. The base structured targets
are built for OSS-Fuzz's AddressSanitizer/i386 configuration. The GnuTLS,
mbedTLS, and HTTP/3 variants are omitted from i386 and MemorySanitizer builds.
The primary local workflow targets Linux and requires Bash, Clang, CMake 3.24 or newer, Python 3, a build tool such as Ninja or Make, and network access for the initial dependency build.
Build all targets against the latest curl source:
./mainline.shUse -c to build a local curl checkout, or -t to build one target:
./mainline.sh -c /path/to/curl
./mainline.sh -t curl_fuzzer_httpThe default AddressSanitizer build creates standalone replay binaries under
build/ and runs the CTest suite. A binary accepts either individual inputs or
directories:
FUZZ_VERBOSE=1 ./build/curl_fuzzer_http \
corpora/curl_fuzzer_http/test_url_http
./build/curl_fuzzer_http corpora/curl_fuzzer_http/These standalone binaries replay inputs; they do not perform mutation fuzzing. See the local fuzzing guide for the OSS-Fuzz workflow and sanitizer options.
The Python package requires Python 3.10 or newer. With
uv:
uv sync
uv run read_corpus corpora/curl_fuzzer_http/test_url_httpOr use a conventional virtual environment:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e .
read_corpus corpora/curl_fuzzer_http/test_url_httpUseful commands include:
read_corpusfor legacy TLV inputs.read_proto_corpusfor binary protobuf scenarios; named fields requireprotoc, but do not require a prior build from a source checkout.generate_corpusfor legacy TLV seeds.tlv_to_protofor converting supported fields from legacy TLV inputs to textproto.generate_decoder_htmlfor the standalone browser decoder for legacy TLV and protobufScenarioinputs. Runnpm ci --ignore-scriptsonce before building it so the pinned protobuf.js runtime can be bundled into the page.compare_fuzzersfor repeatable throughput and source-coverage comparisons between production libFuzzer builds.
Each command supports --help. The complete list of installed entry points is
in pyproject.toml.
Download the public OSS-Fuzz corpora for every currently supported target:
./scripts/download_public_corpus.shInputs are extracted to ossfuzz_corpus/<target>/. Existing non-empty target
directories are retained; pass -f to refresh them. Missing public corpora are
reported and skipped because newly added targets may not have one yet.
Bootstrap the dedicated HTTP/2 corpus from historical Scenario inputs after
downloading them (requires protoc, provided by protobuf-compiler):
uv run bootstrap_http2_corpusThis adds byte-identical inputs whose final explicit CURLOPT_HTTP_VERSION
setting in the first 64 options is prior knowledge (uint_value: 5) to
ossfuzz_corpus/curl_fuzzer_proto_http2/. It excludes HTTP/1 version selections, overridden
version settings, and malformed protobufs. The predicate is conservative;
it does not simulate curl's validation or target normalization. Native inputs
are retained, and the manifest under ossfuzz_corpus/.manifests/ records source,
schema, and selected corpus hashes plus rejected input hashes. Existing corpus
routing picks up this native directory for coverage, benchmarking, and profiling.
The bootstrap is additive. Before refreshing public corpora, use the downloader's
-f flag so the bootstrapped directory does not hide a newly published native
corpus, then rerun the bootstrap. --source accepts repeatable directory, ZIP,
or file inputs; --output-zip writes a reproducible selected-input archive for
a one-time upload to the dedicated OSS-Fuzz target. Keep ZIPs and manifests
outside replay directories. Run uv run bootstrap_http2_corpus --help for paths.
For legacy targets, checked-in binary seeds live in corpora/. For structured
targets, checked-in textproto files under scenarios/ are the source of truth;
CMake generates their binary corpus entries in the build tree.
Build coverage-instrumented targets, replay local and downloaded corpora, and produce text and HTML reports with:
./codecoverage.shThe reports are written to:
build-coverage/coverage/summary.txtbuild-coverage/coverage/html/index.htmlbuild-coverage/coverage/harness-summary.txtbuild-coverage/coverage/harness-html/index.htmlbuild-coverage/coverage/report.mdbuild-coverage/coverage/gaps.json
The existing summary and HTML paths report curl's lib/, include/curl/, and
src/ sources.
The harness reports cover checked-in C++ sources and headers, excluding tests,
the standalone replay engine, generated code, and dependency build trees.
report.md tracks line, function, region, and branch coverage separately for
each source group and ranks remaining gaps. gaps.json preserves exact
covered/total counters; an unavailable metric or empty denominator is not
reported as 100%. The scoped LLVM exports, curl-coverage.json and
harness-coverage.json, support further analysis.
Coverage measures the sources mapped into the selected binaries. Disabled features and other platforms need their own builds, and failures or incomplete corpus replay need investigation before interpreting a result as complete. Compare changes using the same curl revision, toolchain, features, and corpora.
curl_fuzzer_full_coverage runs deterministic, bounded compatibility API and
legacy mutator probes once per process during LLVM coverage or Fuzz Introspector
builds
(ENABLE_COVERAGE=ON or SANITIZER=coverage/introspector). It supplements
corpus replay without adding protocol lanes. Ordinary sanitizer builds do not
package this profiling helper as a fuzz target. Its seed input only triggers
the probes; its contents are ignored.
Mutator probes exercise framing, routing, capacity limits, and crossover on
local byte buffers. They perform no transfers and cover the custom-mutator ABI
adapters that corpus replay normally skips.
The structured curl_fuzzer_proto_api coverage/introspection build also runs
the curl API probes once during initialization, with a first-callback fallback
for standalone runners. Those probes reuse the live proto curl runtime; global
cleanup is deferred until process exit. Retaining the legacy target alongside
this migration allows source replay to measure parity before deletion.
Pass -c /path/to/curl to cover a local curl checkout. The manual Coverage
GitHub Actions workflow publishes the same reports and reuses a public-corpus
cache keyed by ISO week.
Install the Python test dependencies and run the repository checks with:
uv sync --extra python-tests
uv run pytest tests/test_*.py
./lint.shContributor documentation for the legacy and structured harnesses is in the documentation site. See REPRODUCING.md for crash investigation and BENCHMARKING.md for controlled performance and source coverage comparisons, and PROFILING.md for CPU hotspot analysis.
curl-fuzzer is distributed under the curl license. See LICENSE.