Skip to content

chunked: reject nul byte in trailer#21896

Closed
alhudz wants to merge 1 commit into
curl:masterfrom
alhudz:chunked-trailer-nul
Closed

chunked: reject nul byte in trailer#21896
alhudz wants to merge 1 commit into
curl:masterfrom
alhudz:chunked-trailer-nul

Conversation

@alhudz

@alhudz alhudz commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Repro: fetch a chunked HTTP/1 response whose trailer line carries a nul byte, e.g. chunky-trailer: he\0llo. curl currently accepts it and exits 0.
Cause: httpchunk_readwrite() accumulates trailer bytes and forwards them to the client as headers (CLIENTWRITE_TRAILER), so the trailer reaches the CURLOPT_HEADERFUNCTION callback and curl_easy_header(). Regular response headers run through verify_header(), which rejects an embedded nul, but trailers never do, so a server can smuggle a nul into a header delivered to the application.
Fix: reject a nul byte in the trailer parser, matching the verify_header() behaviour for normal headers.

Covered by the new test 2106.

@github-actions github-actions Bot added the tests label Jun 8, 2026
@bagder

bagder commented Jun 8, 2026

Copy link
Copy Markdown
Member

Since verify_header does more checks than so, shouldn't it rather just call that function?

@bagder bagder added the HTTP label Jun 8, 2026
Trailers are delivered to the application as headers via
CLIENTWRITE_TRAILER, but unlike regular response headers they skipped
the verify_header() checks, so a server could smuggle a nul byte (or
stray CR) into a header reaching CURLOPT_HEADERFUNCTION and
curl_easy_header().

Run each assembled trailer line through Curl_verify_header(), the same
validation used for normal headers.

Covered by the new test 2106.
@alhudz alhudz force-pushed the chunked-trailer-nul branch from bfd5b3e to f7cea4c Compare June 8, 2026 11:17
@alhudz

alhudz commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Good point. Exposed verify_header as Curl_verify_header and run each assembled trailer line through it instead of doing a one-off nul check, so trailers now get the same CR and missing-colon checks as regular headers. That also means the rejection returns CURLE_WEIRD_SERVER_REPLY now, matching normal headers, so I updated test 2106 to expect 8. Existing trailer tests 1417, 1116, 1540 and 2105 still pass.

@bagder bagder closed this in 7de0a7e Jun 8, 2026
@bagder

bagder commented Jun 8, 2026

Copy link
Copy Markdown
Member

Thanks!

dkarpov1970 pushed a commit to dkarpov1970/curl that referenced this pull request Jun 9, 2026
Trailers are delivered to the application as headers via
CLIENTWRITE_TRAILER, but unlike regular response headers they skipped
the verify_header() checks, so a server could smuggle a nul byte (or
stray CR) into a header reaching CURLOPT_HEADERFUNCTION and
curl_easy_header().

Run each assembled trailer line through Curl_verify_header(), the same
validation used for normal headers.

Covered by the new test 2106.

Closes curl#21896
dkarpov1970 pushed a commit to dkarpov1970/curl that referenced this pull request Jun 10, 2026
Trailers are delivered to the application as headers via
CLIENTWRITE_TRAILER, but unlike regular response headers they skipped
the verify_header() checks, so a server could smuggle a nul byte (or
stray CR) into a header reaching CURLOPT_HEADERFUNCTION and
curl_easy_header().

Run each assembled trailer line through Curl_verify_header(), the same
validation used for normal headers.

Covered by the new test 2106.

Closes curl#21896
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

2 participants