Skip to content

Harden chunked encoding parsing - #68465

Open
Youssef1313 wants to merge 7 commits into
mainfrom
dev/ygerges/chunked-encoding-fix
Open

Harden chunked encoding parsing#68465
Youssef1313 wants to merge 7 commits into
mainfrom
dev/ygerges/chunked-encoding-fix

Conversation

@Youssef1313

@Youssef1313 Youssef1313 commented Aug 12, 2026

Copy link
Copy Markdown
Member

Fixes #66794

  • It's easier, IMO, to read the whole updated class compared to reading the diff.
  • The removal of the AppContext switch is intentional. It was introduced in older releases servicing, AFAIK, and wasn't intended to ever be in main branch.

@Youssef1313
Youssef1313 force-pushed the dev/ygerges/chunked-encoding-fix branch 5 times, most recently from e128d12 to 462697e Compare August 13, 2026 06:02
@gfoidl gfoidl added the area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions label Aug 13, 2026
@Youssef1313
Youssef1313 force-pushed the dev/ygerges/chunked-encoding-fix branch 4 times, most recently from 4615157 to 6be36e9 Compare August 13, 2026 09:42
@Youssef1313
Youssef1313 marked this pull request as ready for review August 13, 2026 09:53
Copilot AI lite review requested due to automatic review settings August 13, 2026 09:53
@Youssef1313
Youssef1313 force-pushed the dev/ygerges/chunked-encoding-fix branch from 6be36e9 to 92da644 Compare August 13, 2026 09:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens Kestrel’s HTTP/1.1 chunked transfer-encoding parsing to strictly reject invalid chunk extensions (notably CR/LF inside chunk-ext, including quoted-string cases) per RFC 9110/9112, and updates tests accordingly. It also removes the legacy insecure chunked parsing AppContext switch from the mainline implementation.

Changes:

  • Tighten Http1ChunkedEncodingMessageBody chunk-size and chunk-extension parsing (including stricter token/quoted-string validation and CRLF handling).
  • Expand/adjust chunked request test coverage for invalid/valid extension forms and boundary splitting.
  • Update existing tests’ chunk-extension strings to avoid now-invalid characters (e.g., spaces in tokens).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs Reworks chunk-size and chunk-ext parsing to be stricter and RFC-aligned (including quoted-string rules).
src/Servers/Kestrel/Core/test/MessageBodyTests.cs Adds new tests for incomplete chunk-extension/value parsing and max hex digit chunk-size parsing.
src/Servers/Kestrel/test/InMemory.FunctionalTests/ChunkedRequestTests.cs Expands valid/invalid chunk extension theory cases; updates extension examples; adjusts RemoteExecutor options.
src/Servers/Kestrel/test/InMemory.FunctionalTests/MaxRequestBodySizeTests.cs Updates chunk-extension strings in payloads to remain valid under stricter parsing.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Servers/Kestrel/Core/test/MessageBodyTests.cs
Comment thread src/Servers/Kestrel/test/InMemory.FunctionalTests/ChunkedRequestTests.cs Outdated
@Youssef1313
Youssef1313 force-pushed the dev/ygerges/chunked-encoding-fix branch from 92da644 to ff7097f Compare August 13, 2026 11:45

@cincuranet cincuranet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With MaxRequestBodySize = 10 and

Transfer-Encoding: chunked\r\n
\r\n
5;a="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

the 413 is not returned.

With 5;a="aaaaaaaaaaaa... ← 1 MB of 'a', no closing quote, no CRLF, written in 64-byte segments, because the code never advances consumed until a whole ;name[=value] unit parses, every arrival re-scans the entire pending extension. Serious CPU burn. The quoted variant is worst because it's parsed byte-at-a-time through a Func<byte,bool> delegate.

Finally, I'm missing tests for stuff like 2;a="x\r\ny"\r\n and 2;a="x\<CR>"\r\n.

[InlineData("2;novalue\r\nxy\r\n0")] // Name only chunk extension
//[InlineData("2 ;\r\nxy\r\n0")] // Technically allowed per spec, but we never supported it, and no one should be sending it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to allow this now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm implementing the spec completely and trying to be 100% compliant. I think that's best.

@Youssef1313

Copy link
Copy Markdown
Member Author

With MaxRequestBodySize = 10 and

Transfer-Encoding: chunked\r\n
\r\n
5;a="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

the 413 is not returned.

With 5;a="aaaaaaaaaaaa... ← 1 MB of 'a', no closing quote, no CRLF, written in 64-byte segments, because the code never advances consumed until a whole ;name[=value] unit parses, every arrival re-scans the entire pending extension. Serious CPU burn. The quoted variant is worst because it's parsed byte-at-a-time through a Func<byte,bool> delegate.

Finally, I'm missing tests for stuff like 2;a="x\r\ny"\r\n and 2;a="x\<CR>"\r\n.

Let me see what's the best way to handle this. I guess we will need more "internal" modes to allow us to track where we were at in parsing extensions and then we can consume things more frequently.

@DeagleGross DeagleGross left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jiri raised a great point about code re-reading the whole payload on the next iteration; but I think we can do in the follow-up PR - this PR can focus on char rejection only. If you decide to do in follow-up - lets create a separate issue to track this?

If i would be doing this, i would create a struct that holds mode, readOffset (which keeps track of how much data we already parsed from the input buffer), and chunkSize which does not require us re-reading the first byte to determine size. Then you can continue from last not read byte.

@@ -28,8 +38,6 @@ internal sealed class Http1ChunkedEncodingMessageBody : Http1MessageBody
private readonly Pipe _requestBodyPipe;
private ReadResult _readResult;

private static readonly bool InsecureChunkedParsing = AppContext.TryGetSwitch("Microsoft.AspNetCore.Server.Kestrel.EnableInsecureChunkedRequestParsing", out var value) && value;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Youssef1313 Youssef1313 Aug 18, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc getting updated in dotnet/AspNetCore.Docs#37484

// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
// / DIGIT / ALPHA
// ; any VCHAR, except delimiters
private static ReadOnlySpan<byte> s_tchar => "!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"u8;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can reference

private static readonly SearchValues<byte> _allowedTokenBytes = SearchValues.Create("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"u8);

IndexOfInvalidTokenChar() already gives you vectorized lookup.

Comment thread src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs Outdated
@Youssef1313
Youssef1313 requested a review from SamMonoRT as a code owner August 18, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harden CR/LF handling when parsing chunked extension

5 participants