Skip to content

Fix UnsupportedOperationException in readTrailingHeaders#16412

Merged
normanmaurer merged 1 commit intonetty:4.2from
furkanvarol:4.2
Mar 12, 2026
Merged

Fix UnsupportedOperationException in readTrailingHeaders#16412
normanmaurer merged 1 commit intonetty:4.2from
furkanvarol:4.2

Conversation

@furkanvarol
Copy link
Copy Markdown
Contributor

Motivation:

When a chunked HTTP message contains a trailer field with a folded value (obs-fold continuation line starting with SP or HT), readTrailingHeaders threw UnsupportedOperationException. This happened because the folding logic called List.set() on the list returned by trailingHeaders().getAll(), which is an AbstractList implementation that does not override set().

Modifications:

Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded continuation lines into the instance-level value field, mirroring the pattern already used by readHeaders. The previous header is now flushed via trailingHeaders().add() only once its complete value is assembled, eliminating the need to mutate the list returned by getAll(). Forbidden trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered at flush time, consistent with the previous behaviour.

Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest covering:

  • A single trailer field
  • A folded trailer field with multiple SP and HT continuation lines, followed by a non-folded trailer to verify isolation between fields
  • Forbidden trailer fields interleaved with a valid one, with a forbidden field placed last to exercise the post-loop flush path

Result:

Chunked HTTP messages with folded trailer values are now decoded correctly instead of throwing UnsupportedOperationException.

@normanmaurer normanmaurer requested review from chrisvest and yawkat March 5, 2026 15:56
@normanmaurer normanmaurer added this to the 4.2.11.Final milestone Mar 5, 2026
@normanmaurer
Copy link
Copy Markdown
Member

Great catch!

@normanmaurer normanmaurer added needs-cherry-pick-4.1 This PR should be cherry-picked to 4.1 once merged. needs-cherry-pick-5.0 This PR should be cherry-picked to 5.0 once merged. labels Mar 6, 2026
@furkanvarol
Copy link
Copy Markdown
Contributor Author

furkanvarol commented Mar 6, 2026

Thanks! Happy to contribute :)

@normanmaurer
Copy link
Copy Markdown
Member

@vietj as well... PTAL

Copy link
Copy Markdown
Contributor

@yawkat yawkat left a comment

Choose a reason for hiding this comment

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

Looks fine in principle, but this is dangerous code to touch… It's a niche feature, it relates to HTTP/1.1 parsing, the patch expands the permitted inputs. That's the perfect mix for a new request smuggling vuln

@furkanvarol
Copy link
Copy Markdown
Contributor Author

Hey @yawkat, thanks for reviewing the PR. I agree it is a niche feature and I'm not sure anyone is actively using trailers with obs-folding in practice.

Regarding your comment about expanding permitted inputs — could you clarify what you mean? The goal of this fix is not to widen the accepted input space, but to correctly implement what the original code already intended to support. The previous logic explicitly handled obs-folding in trailers, it just crashed with an UnsupportedOperationException due to calling List.set() on an AbstractList that does not override it. So the permitted inputs are unchanged in intent — the fix just makes the implementation match that intent.

Motivation:

When a chunked HTTP message contains a trailer field with a folded value
(obs-fold continuation line starting with SP or HT), readTrailingHeaders
threw UnsupportedOperationException. This happened because the folding
logic called List.set() on the list returned by trailingHeaders().getAll(),
which is an AbstractList implementation that does not override set().

Modifications:

Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded
continuation lines into the instance-level `value` field, mirroring the
pattern already used by readHeaders. The previous header is now flushed
via trailingHeaders().add() only once its complete value is assembled,
eliminating the need to mutate the list returned by getAll(). Forbidden
trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered
at flush time, consistent with the previous behaviour.

Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest covering:
- A single trailer field
- A folded trailer field with multiple SP and HT continuation lines,
  followed by a non-folded trailer to verify isolation between fields
- Forbidden trailer fields interleaved with a valid one, with a forbidden
  field placed last to exercise the post-loop flush path

Result:

Chunked HTTP messages with folded trailer values are now decoded correctly
instead of throwing UnsupportedOperationException.
@yawkat
Copy link
Copy Markdown
Contributor

yawkat commented Mar 11, 2026

I understand the previous code was buggy, but that behavior may have protected us against more exotic request smuggling. Fixing it essentially does broaden the scope of accepted inputs.

Not saying the code should be left as-is, but it is dangerous to modify

@normanmaurer
Copy link
Copy Markdown
Member

Hey @yawkat, thanks for reviewing the PR. I agree it is a niche feature and I'm not sure anyone is actively using trailers with obs-folding in practice.

Regarding your comment about expanding permitted inputs — could you clarify what you mean? The goal of this fix is not to widen the accepted input space, but to correctly implement what the original code already intended to support. The previous logic explicitly handled obs-folding in trailers, it just crashed with an UnsupportedOperationException due to calling List.set() on an AbstractList that does not override it. So the permitted inputs are unchanged in intent — the fix just makes the implementation match that intent.

I think we should merge this...

@furkanvarol did you sign our ICLA yet ? https://netty.io/s/icla

@furkanvarol
Copy link
Copy Markdown
Contributor Author

@yawkat Understood — though if we decide obs-folding in trailers should not be supported, that should be an explicit rejection (e.g. a decode error) rather than relying on an accidental crash as the enforcement mechanism. Happy to go that route if preferred.

@normanmaurer yes, I already signed it :)

@normanmaurer normanmaurer merged commit 9a85f9f into netty:4.2 Mar 12, 2026
34 of 35 checks passed
@netty-project-bot
Copy link
Copy Markdown
Contributor

Could not create auto-port PR.
Got conflicts when cherry-picking onto 5.0.

netty-project-bot pushed a commit that referenced this pull request Mar 12, 2026
Motivation:

When a chunked HTTP message contains a trailer field with a folded value
(obs-fold continuation line starting with SP or HT), readTrailingHeaders
threw UnsupportedOperationException. This happened because the folding
logic called List.set() on the list returned by
trailingHeaders().getAll(), which is an AbstractList implementation that
does not override set().

Modifications:

Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded
continuation lines into the instance-level `value` field, mirroring the
pattern already used by readHeaders. The previous header is now flushed
via trailingHeaders().add() only once its complete value is assembled,
eliminating the need to mutate the list returned by getAll(). Forbidden
trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered
at flush time, consistent with the previous behaviour.

Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest
covering:
- A single trailer field
- A folded trailer field with multiple SP and HT continuation lines,
followed by a non-folded trailer to verify isolation between fields
- Forbidden trailer fields interleaved with a valid one, with a
forbidden field placed last to exercise the post-loop flush path

Result:

Chunked HTTP messages with folded trailer values are now decoded
correctly instead of throwing UnsupportedOperationException.

(cherry picked from commit 9a85f9f)
@netty-project-bot
Copy link
Copy Markdown
Contributor

Auto-port PR for 4.1: #16437

@github-actions github-actions bot removed the needs-cherry-pick-4.1 This PR should be cherry-picked to 4.1 once merged. label Mar 12, 2026
normanmaurer pushed a commit that referenced this pull request Mar 12, 2026
…rs (#16437)

Auto-port of #16412 to 4.1
Cherry-picked commit: 9a85f9f

---
Motivation:

When a chunked HTTP message contains a trailer field with a folded value
(obs-fold continuation line starting with SP or HT), readTrailingHeaders
threw UnsupportedOperationException. This happened because the folding
logic called List.set() on the list returned by
trailingHeaders().getAll(), which is an AbstractList implementation that
does not override set().

Modifications:

Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded
continuation lines into the instance-level `value` field, mirroring the
pattern already used by readHeaders. The previous header is now flushed
via trailingHeaders().add() only once its complete value is assembled,
eliminating the need to mutate the list returned by getAll(). Forbidden
trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered
at flush time, consistent with the previous behaviour.

Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest
covering:
- A single trailer field
- A folded trailer field with multiple SP and HT continuation lines,
followed by a non-folded trailer to verify isolation between fields
- Forbidden trailer fields interleaved with a valid one, with a
forbidden field placed last to exercise the post-loop flush path

Result:

Chunked HTTP messages with folded trailer values are now decoded
correctly instead of throwing UnsupportedOperationException.

Co-authored-by: Furkan Varol <furkanvarol@users.noreply.github.com>
chrisvest pushed a commit to chrisvest/netty that referenced this pull request Mar 12, 2026
Motivation:

When a chunked HTTP message contains a trailer field with a folded value
(obs-fold continuation line starting with SP or HT), readTrailingHeaders
threw UnsupportedOperationException. This happened because the folding
logic called List.set() on the list returned by
trailingHeaders().getAll(), which is an AbstractList implementation that
does not override set().

Modifications:

Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded
continuation lines into the instance-level `value` field, mirroring the
pattern already used by readHeaders. The previous header is now flushed
via trailingHeaders().add() only once its complete value is assembled,
eliminating the need to mutate the list returned by getAll(). Forbidden
trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered
at flush time, consistent with the previous behaviour.

Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest
covering:
- A single trailer field
- A folded trailer field with multiple SP and HT continuation lines,
followed by a non-folded trailer to verify isolation between fields
- Forbidden trailer fields interleaved with a valid one, with a
forbidden field placed last to exercise the post-loop flush path

Result:

Chunked HTTP messages with folded trailer values are now decoded
correctly instead of throwing UnsupportedOperationException.

(cherry picked from commit 9a85f9f)
@chrisvest
Copy link
Copy Markdown
Member

Port to 5.0: #16453

@chrisvest chrisvest removed the needs-cherry-pick-5.0 This PR should be cherry-picked to 5.0 once merged. label Mar 12, 2026
normanmaurer pushed a commit that referenced this pull request Mar 13, 2026
…6453)

Motivation:

When a chunked HTTP message contains a trailer field with a folded value
(obs-fold continuation line starting with SP or HT), readTrailingHeaders
threw UnsupportedOperationException. This happened because the folding
logic called List.set() on the list returned by
trailingHeaders().getAll(), which is an AbstractList implementation that
does not override set().

Modifications:

Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded
continuation lines into the instance-level `value` field, mirroring the
pattern already used by readHeaders. The previous header is now flushed
via trailingHeaders().add() only once its complete value is assembled,
eliminating the need to mutate the list returned by getAll(). Forbidden
trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered
at flush time, consistent with the previous behaviour.

Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest
covering:
- A single trailer field
- A folded trailer field with multiple SP and HT continuation lines,
followed by a non-folded trailer to verify isolation between fields
- Forbidden trailer fields interleaved with a valid one, with a
forbidden field placed last to exercise the post-loop flush path

Result:

Chunked HTTP messages with folded trailer values are now decoded
correctly instead of throwing UnsupportedOperationException.

(cherry picked from commit 9a85f9f)

Co-authored-by: Furkan Varol <furkanvarol@users.noreply.github.com>
dongjoon-hyun added a commit to apache/spark-kubernetes-operator that referenced this pull request Mar 25, 2026
### What changes were proposed in this pull request?

This PR upgrades `Netty` to 4.2.12.Final.

### Why are the changes needed?

To bring in the latest bug fixes.
- https://netty.io/news/2026/03/24/4-2-12-Final.html
  - netty/netty#16550
- https://netty.io/news/2026/03/24/4-2-11-Final.html
  - netty/netty#16489
  - netty/netty#16536
  - netty/netty#16412

### Does this PR introduce _any_ user-facing change?

No.

### How was this patch tested?

CI should pass with the existing tests.

### Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 4.6)

Closes #589 from dongjoon-hyun/SPARK-56213.

Authored-by: Dongjoon Hyun <dongjoon@apache.org>
Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
dongjoon-hyun added a commit to apache/spark that referenced this pull request Mar 26, 2026
### What changes were proposed in this pull request?

This PR upgrades `Netty` to 4.2.12.Final.

### Why are the changes needed?

To bring in the latest bug fixes.
- https://netty.io/news/2026/03/24/4-2-12-Final.html
  - netty/netty#16550
- https://netty.io/news/2026/03/24/4-2-11-Final.html
  - netty/netty#16489
  - netty/netty#16536
  - netty/netty#16412

### Does this PR introduce _any_ user-facing change?

No.

### How was this patch tested?

CI should pass with the existing tests.

### Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 4.6)

Closes #55016 from dongjoon-hyun/SPARK-56214.

Authored-by: Dongjoon Hyun <dongjoon@apache.org>
Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
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.

6 participants