Skip to content

nginx: add fuzz harnesses for HTTP parsers, HTTP/2, PROXY protocol, upstream, DNS resolver#61

Open
tc-agent wants to merge 2 commits into
masterfrom
nginx-add-parser-fuzzers
Open

nginx: add fuzz harnesses for HTTP parsers, HTTP/2, PROXY protocol, upstream, DNS resolver#61
tc-agent wants to merge 2 commits into
masterfrom
nginx-add-parser-fuzzers

Conversation

@tc-agent

@tc-agent tc-agent commented May 13, 2026

Copy link
Copy Markdown
Owner

Summary

Adds six direct-call libFuzzer harnesses + a small in-build source-exposure patch, targeting parsing and protocol code that the existing http_request_fuzzer covers little or not at all.

Harnesses

  1. h2_fuzzer — brings up an in-process nginx cycle with an http2-enabled unix listener, prepends the HTTP/2 connection preface to each fuzz input. Drives ngx_http_v2.c, ngx_http_v2_table.c (HPACK), ngx_http_v2_filter_module.c. Config enables gzip / charset / userid / add_header on the server and gives the H2 layer 8 free connections so stream allocation + the response filter chain are also exercised.
  2. upstream_fuzzer — same cycle pattern as http_request_fuzzer, but feeds a fixed valid HTTP/1.1 request from the client side and fuzzes the upstream HTTP/1 response. Targets ngx_http_upstream.c, ngx_http_proxy_module.c, and the response filter chain. Ships upstream_fuzzer.options disabling leak detection — the leak path in ngx_http_upstream_connect is shared with http_request_fuzzer and is tracked in google/oss-fuzz#15401.
  3. parser_fuzzer — 13-way selector multiplexing of ngx_http_parse_request_line(), ngx_http_parse_header_line(), ngx_http_parse_status_line(), ngx_http_parse_chunked(), ngx_http_parse_complex_uri(), ngx_http_parse_unsafe_uri(), ngx_http_parse_uri(), ngx_http_arg(), ngx_http_split_args(), ngx_http_parse_multi_header_lines(), ngx_http_parse_cookie_lines(), ngx_http_parse_set_cookie_lines() (src/http/ngx_http_parse.c).
  4. pp_fuzzer — drives ngx_proxy_protocol_read() and ngx_proxy_protocol_get_tlv() for v1 (text) and v2 (binary) PROXY protocol variants, including v2 TLV decoding (src/core/ngx_proxy_protocol.c).
  5. inet_fuzzer — drives ngx_inet_addr, ngx_inet6_addr, ngx_ptocidr, ngx_parse_addr, ngx_parse_addr_port, ngx_parse_url, the ngx_atoi/ngx_hextoi/ngx_atotm family, ngx_unescape_uri, ngx_escape_uri and ngx_decode_base64 (src/core/ngx_inet.c, src/core/ngx_string.c).
  6. resolver_fuzzer — drives ngx_resolver_process_response() directly with fuzzed DNS packets against a minimally-mocked ngx_resolver_t. build.sh carries an inline source patch that removes static from ngx_resolver_process_response/_a/_srv/_ptr and ngx_resolver_copy so the harness can link against them. These five functions are the bug-rich entry points historically responsible for resolver CVEs (e.g. CVE-2016-0742).

Each ships a seed corpus generated from human-readable code in build.sh (no binary blobs) and a libFuzzer dictionary. make_fuzzers and build.sh are extended to compile and seed the new binaries against the same nginx object set as the existing http_request_fuzzer. The existing http_request_fuzzer is not modified.

Coverage

Baseline numbers are from Fuzz Introspector; per-file FI numbers for nginx have known accuracy caveats (google/oss-fuzz#13267, google/oss-fuzz#13413). After-numbers come from a single fuzz_for_pr --minutes 5 run followed by an LLVM coverage build with all seven harnesses running concurrently.

File FI baseline After (5 min)
Global 13.32% (12926/97063) 20.2% (19894/98422)
src/http/ngx_http_parse.c 41.7% 98.0% (1743/1778)
src/http/v2/ngx_http_v2.c 0% 62.4% (2049/3285)
src/http/v2/ngx_http_v2_table.c 0% 73.8% (141/191)
src/http/v2/ngx_http_v2_filter_module.c 5.7% 44.2% (506/1144)
src/core/ngx_proxy_protocol.c 0% 82.2% (295/359)
src/core/ngx_inet.c ~27% 71.4% (692/969)
src/core/ngx_string.c ~35% 51.6% (669/1296)
src/core/ngx_resolver.c 1.8% 12.3% (374/3040)
src/http/ngx_http_upstream.c 5.6% 25.5% (1063/4175)
src/http/modules/ngx_http_proxy_module.c 14.9% 36.7% (1035/2822)
src/http/ngx_http_request.c ~34% 48.7% (1043/2142)
src/http/modules/ngx_http_userid_filter_module.c 13.7% 35.5% (160/451)

The largest unreached files remaining (ngx_resolver.c, ngx_http_grpc_module.c, ngx_http_ssi_filter_module.c, the SSL/mail/stream subsystems) require build or runtime configuration outside this PR's scope (TLS, grpc_pass, response generators that trigger specific filters), which bounds how far global line coverage can move in a single integration change; the per-subsystem deltas above are the more representative signal of this PR's effect.

Per-harness 5-minute libFuzzer summary (cov = unique edges):

Harness Iterations Edges Corpus
h2_fuzzer 1.4M 1260 982
parser_fuzzer 7.2M 709 1580
upstream_fuzzer ~1.5M 1097 101
pp_fuzzer 9.2M 182 177
inet_fuzzer 1.6M 415 470
http_request_fuzzer 2.0M 456 221
resolver_fuzzer 6.2M 146 98

Why the harnesses are bundled

The six harnesses are submitted together because they share build-system plumbing (make_fuzzers rules, build.sh seed generation, the same nginx object set). Splitting would create churn against the same files in each follow-up PR. Happy to split if reviewers prefer.

@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Fuzzing Coverage Report

Tested: project nginx · base 26deef8 → head 969f50f · 300s total fuzz budget · updated 2026-05-27 19:14 UTC · workflow run

Metric Before After Delta
Static reachability >45m >45m
Line coverage 13.0% (12672/97755) 21.5% (21173/98441) +67.1%
Branch coverage 14.2% (4684/33004) 24.4% (8125/33242) +73.5%
Function coverage 7.6% (516/6813) 11.4% (778/6852) +50.8%

Per-harness

Harness Lines before Lines after Δ
h2_fuzzer 0% 16.9% (12641/74842) new
http_request_fuzzer 13.0% (12672/97755) 12.4% (12108/97755) -4.5%
inet_fuzzer 0% 2.0% (1501/74817) new
parser_fuzzer 0% 3.2% (2389/74914) new
pp_fuzzer 0% 1.1% (820/74747) new
resolver_fuzzer 0% 1.1% (858/74765) new
upstream_fuzzer 0% 17.5% (13107/74867) new

Δ = (after − before) / before, to accommodate that denominators may change. "new" when before is 0; "deleted" when after is 0.

@tc-agent
tc-agent force-pushed the nginx-add-parser-fuzzers branch from 3031ad7 to f6e05c0 Compare May 13, 2026 18:37
@tc-agent tc-agent changed the title nginx: add fuzz harnesses for HTTP parsers, PROXY protocol, inet/string helpers nginx: add fuzz harnesses for HTTP parsers, HTTP/2, PROXY protocol, inet/string helpers May 13, 2026
@tc-agent
tc-agent force-pushed the nginx-add-parser-fuzzers branch from f6e05c0 to 0833157 Compare May 13, 2026 19:48
@tc-agent
tc-agent force-pushed the master branch 2 times, most recently from 07a0cf6 to ad01771 Compare May 14, 2026 09:51
@tc-agent
tc-agent force-pushed the nginx-add-parser-fuzzers branch from 0833157 to 360a72a Compare May 14, 2026 09:51
@tc-agent tc-agent changed the title nginx: add fuzz harnesses for HTTP parsers, HTTP/2, PROXY protocol, inet/string helpers nginx: add fuzz harnesses for HTTP parsers, HTTP/2, PROXY protocol, upstream, DNS resolver May 14, 2026
@tc-agent
tc-agent force-pushed the master branch 8 times, most recently from 64e909c to 77ade48 Compare May 16, 2026 15:20
@tc-agent
tc-agent force-pushed the nginx-add-parser-fuzzers branch from 360a72a to c3ae2b9 Compare May 16, 2026 15:20
@tc-agent
tc-agent force-pushed the master branch 6 times, most recently from b54c0d2 to 1b028e2 Compare May 17, 2026 09:43
@tc-agent
tc-agent force-pushed the nginx-add-parser-fuzzers branch from c3ae2b9 to 5f092fe Compare May 17, 2026 09:43
@tc-agent
tc-agent force-pushed the master branch 2 times, most recently from 5573df7 to 9a36aa8 Compare May 21, 2026 05:13
@tc-agent
tc-agent force-pushed the master branch 4 times, most recently from 6125235 to e512e7e Compare May 27, 2026 06:33
@tc-agent
tc-agent force-pushed the nginx-add-parser-fuzzers branch from 5f092fe to d2f0145 Compare May 27, 2026 07:14
@tc-agent
tc-agent force-pushed the master branch 5 times, most recently from b5fd10c to 712a52f Compare May 27, 2026 16:16
@tc-agent
tc-agent force-pushed the nginx-add-parser-fuzzers branch from d2f0145 to 969f50f Compare May 27, 2026 16:45
@tc-agent
tc-agent force-pushed the master branch 6 times, most recently from af7f9a7 to 806b281 Compare May 28, 2026 18:05
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.

1 participant