Repository navigation
fix(http2): bound peer-controlled streams and headers - #1958
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
|
Can you link an issue this fixes, or provide context on this PR? |
There is no public issue to link because I originally reported these findings privately through the channel specified by the Dart security policy, using g.co/vulnz. The team reviewing the report confirmed that I could proceed with a public PR directly. The PR addresses two related per-connection resource-exhaustion issues in
Both issues are remotely triggerable before application-level limits can reliably reject the work. They are included in the same PR because the fixes share the connection-level admission and header-processing paths. |
|
Thanks, I'll take a look! |
01e157d to
495d46e
Compare
|
LGTM - Any comments @brianquinlan ? |
|
I think that this partially overlaps the merged #1965 @DubaiChewyCookie could you take a look at those two PRs and update your PR to make sure that you capture their functionality. @mosuem I'm not an expert in this area and am nervous about reviewing code ;-) |
495d46e to
993c2ed
Compare
|
Currently updated and rebased. |
SETTINGS_MAX_CONCURRENT_STREAMS was only consulted when this endpoint created local streams. A peer could ignore the advertised setting and open unbounded streams, allocating stream state, queues, windows, and controllers. Enforce the configured peer-initiated stream limit locally before creating Http2StreamImpl. Track active peer streams and locally bound reserved PUSH_PROMISE streams as well. Reject excess streams with RST_STREAM REFUSED_STREAM without publishing them through incomingStreams or peerPushes. Once the peer has acknowledged the advertised stream limit, terminate the connection with GOAWAY ENHANCE_YOUR_CALM after the configured number of consecutive violations. Excess streams received before acknowledgement are still refused but do not count toward the abuse threshold. Disabled server push is also enforced on receipt instead of relying on peer compliance or SETTINGS acknowledgement. Inbound HEADERS and CONTINUATION fragments were accumulated without a size or time bound. Each continuation copied the preceding block again, making fragmented field-block assembly quadratic. HPACK also retained the complete decoded header list before application-level limits could run. Add the following secure defaults per connection: - 100 peer-initiated streams - 16 KiB compressed field block - 8 KiB decoded field section - 16 CONTINUATION frames per field block - 10 second absolute field-block timeout - 8 consecutive post-acknowledgement stream-limit violations Start the field-block timer when the initial HEADERS or PUSH_PROMISE frame header is parsed, before its payload is complete. Retain fragments as chunks and combine them once, enforcing compressed-size and CONTINUATION-count limits while the block is received. Enforce the decoded field-section limit during HPACK processing using name length + value length + 32 bytes per field. Once the limit is crossed, release retained application-visible headers and continue decoding in discard mode so dynamic-table updates remain synchronized. Reject a completely decoded oversized field section with RST_STREAM ENHANCE_YOUR_CALM and do not publish its headers. Compressed-size, CONTINUATION-count, and timeout violations terminate the connection with GOAWAY ENHANCE_YOUR_CALM because the shared HPACK context cannot safely continue without complete decompression. Invalid HPACK encoding terminates the connection with COMPRESSION_ERROR. Advertise SETTINGS_MAX_HEADER_LIST_SIZE while enforcing the configured limit locally because the setting is advisory. This also resolves the unbounded buffering TODO introduced by fdaeafb. Tests cover ACK-independent stream admission, pre-ACK rejection without abuse counting, post-ACK repeated abuse, exact N and N+1 limits, active and reserved stream recovery, disabled push, compressed and decoded size boundaries, many continuations, slow initial payloads, Huffman and indexed amplification, HPACK dynamic-table synchronization, HEADERS, trailers, and PUSH_PROMISE. RFC 9113 sections 4.3, 5.1.2, 6.5.2, and 10.5: https://www.rfc-editor.org/rfc/rfc9113.html CERT/CC VU#421644: https://kb.cert.org/vuls/id/421644
993c2ed to
1dea01e
Compare
|
@mosuem this looks OK to me, shall we merge? |
Revisions updated by `dart tools/rev_sdk_deps.dart`. dartdoc (https://github.com/dart-lang/dartdoc/compare/df2f2cd..c68debc): c68debcb Mon Sep 21 11:07:33 2026 +0200 Sarah Zakarias Adapt the {@example} code fence to the example content (dart-lang/dartdoc#4285) ecosystem (https://github.com/dart-lang/ecosystem/compare/1bdafd8..16dca7a): 16dca7a Mon Sep 21 08:04:10 2026 -0700 Kevin Moore fix(firehose): keep dart_apitool crash details in logs and sanitize breaking table row (dart-lang/ecosystem#461) http (https://github.com/dart-lang/http/compare/35769ba..d8e8e9c): d8e8e9c Mon Sep 21 18:26:27 2026 +0900 DubaiChewyCookie fix(http2): bound peer-controlled streams and headers (dart-lang/http#1958) 286037b Thu Sep 17 17:12:02 2026 -0700 Brian Quinlan chore(http2): prepare to release 3.1.0 (dart-lang/http#1987) a067c94 Wed Sep 16 15:57:52 2026 -0700 Brian Quinlan fix(cupertino_http): possible URLRequest memory leaks (dart-lang/http#1989) 0d15438 Thu Sep 17 00:43:43 2026 +0300 Ellet chore(ok_http): Built-in Kotlin and resolve AGP 9 warnings (dart-lang/http#1973) native (https://github.com/dart-lang/native/compare/a88aaaf..f628b26): f628b26f0 Mon Sep 21 23:21:05 2026 +0200 Daco Harkes [hooks] Add unit tests for `ProtocolExtension` defaults and `build`/`link` error paths (dart-lang/native#3668) 474a93f39 Mon Sep 21 08:01:13 2026 +0500 Muhammad Anas Naveed [jni][jnigen] Update Groovy Gradle property assignments (dart-lang/native#3678) 4434b7f3c Mon Sep 21 10:31:09 2026 +1000 Liam Appelbe [jni] Ignore coverage of generated files (dart-lang/native#3665) b66dfdeae Mon Sep 21 10:27:27 2026 +1000 Liam Appelbe [objective_c] Use `objc_retainAutoreleasedReturnValue` (dart-lang/native#3664) 541121b4b Fri Sep 18 17:30:03 2026 +0200 Daco Harkes [record_use] Add status and coverage badges to `README.md` (dart-lang/native#3675) b97663824 Fri Sep 18 17:25:10 2026 +0200 Daco Harkes [hooks] Prepare `prebuilt_assets_example` v0.1.2 for release (dart-lang/native#3672) 876145f0c Fri Sep 18 15:23:34 2026 +0200 Daco Harkes [hooks] Use `prebuilt/` instead of `assets/` in `prebuilt_assets_example` (dart-lang/native#3671) 73bd0b7b3 Fri Sep 18 12:58:36 2026 +0200 Daco Harkes [hooks] Prepare `prebuilt_assets_example` v0.1.1 for release (dart-lang/native#3669) 4e0f1983f Fri Sep 18 11:40:30 2026 +0200 Daco Harkes [hooks] Add example for bundling prebuilt assets in `pub publish` (dart-lang/native#3637) 22d7cf8da Fri Sep 18 09:28:25 2026 +0200 Daco Harkes [native_test_helpers] Add unit tests for `yamlToJson` and `findPackageRoot` (dart-lang/native#3662) 74e631a8b Fri Sep 18 09:28:25 2026 +0200 Daco Harkes [hooks] Add unit tests for `link` API and `MetadataAsset` (dart-lang/native#3661) 3428cb968 Fri Sep 18 09:28:24 2026 +0200 Daco Harkes [data_assets] Add unit tests for `LinkOutput` data asset extensions (dart-lang/native#3660) 0e4eea612 Fri Sep 18 09:28:24 2026 +0200 Daco Harkes [code_assets] Add unit tests for `testCodeBuildHook` and `CCompilerConfig` (dart-lang/native#3659) dc44c5383 Fri Sep 18 09:28:24 2026 +0200 Daco Harkes [ffi] Add unit tests for `Arena` error handling and `malloc.free` (dart-lang/native#3658) 1e1e2fb8a Fri Sep 18 09:28:23 2026 +0200 Daco Harkes [native_toolchain_c] Default `linkerOptions` in `CLinker.run` and add `CLibrary` tests (dart-lang/native#3657) f3c066006 Fri Sep 18 09:28:23 2026 +0200 Daco Harkes [data_assets] [hooks] [hooks_runner] [record_use] Add coverage ignore comments for debug methods (dart-lang/native#3656) 59cd44422 Fri Sep 18 09:28:22 2026 +0200 Daco Harkes [infra] Clean coverage directory and enable `--check-ignore` in `tool/ci.dart` (dart-lang/native#3655) b859d5fa8 Fri Sep 18 09:35:46 2026 +0500 Muhammad Anas Naveed [ffigen] Default compiler options for C++ (dart-lang/native#3645) eb6ea1e56 Thu Sep 17 09:28:38 2026 +1000 Liam Appelbe [ffigen][jnigen] More docs (dart-lang/native#3649) da786131c Thu Sep 17 09:06:20 2026 +1000 Liam Appelbe [ffigen][jnigen] YAML migration skills (dart-lang/native#3648) fc0491997 Wed Sep 16 15:37:19 2026 -0700 Brian Quinlan [objective_c] don't use autorelease for `toNSDate` (dart-lang/native#3650) f7f9a4556 Wed Sep 16 17:32:47 2026 +0200 Daco Harkes [code_assets] [hooks_runner] [native_toolchain_c] Prepare for release (dart-lang/native#3642) 1c60c437c Wed Sep 16 13:40:31 2026 +0200 Daco Harkes [hooks_runner] Restore non-breaking `KernelAssetPath.toJson` and add `toJsonForTarget` (dart-lang/native#3643) 7667162b3 Wed Sep 16 11:46:18 2026 +0200 Daco Harkes [infra] Use `dependency_validator` in `tool/ci.dart` (dart-lang/native#3646) f6d153d01 Tue Sep 15 19:32:08 2026 -0700 Brian Quinlan [objective_c] Fix potential leaks in converts (dart-lang/native#3647) cb2c62331 Wed Sep 16 11:32:42 2026 +1000 Liam Appelbe [ffigen] Remove YAML from readmes (dart-lang/native#3633) 22e835728 Wed Sep 16 09:49:02 2026 +1000 Liam Appelbe [jnigen] Remove yaml from readme (dart-lang/native#3634) eb444eee2 Wed Sep 16 09:48:04 2026 +1000 Liam Appelbe [infra] Add and fix `unawaited_futures` and `discarded_futures` lints (dart-lang/native#3639) 0f419f0d2 Wed Sep 16 09:47:44 2026 +1000 Liam Appelbe [infra] Switch from coveralls to codecov (dart-lang/native#3626) shelf (https://github.com/dart-lang/shelf/compare/f36dd68..e5c8dc6): e5c8dc6 Tue Sep 15 18:15:41 2026 -0700 Kevin Moore fix(shelf_static): ignore suffix ranges selecting zero bytes (dart-lang/shelf#541) test (https://github.com/dart-lang/test/compare/c80d53d..9946c95): 9946c955 Mon Sep 21 12:31:54 2026 -0700 Nate Bosch Fix a race in the frontend server client fake (dart-lang/test#2762) 48c3fdf6 Fri Sep 18 18:52:52 2026 -0700 Nate Bosch Fix race conditions during test load (dart-lang/test#2761) 5d74864b Fri Sep 18 16:25:57 2026 -0700 Nate Bosch Remove mentions of Travis (dart-lang/test#2760) 5c4d9b25 Fri Sep 18 12:49:00 2026 -0700 Nate Bosch Use consistent *Checks naming for prints (dart-lang/test#2759) f927df26 Fri Sep 18 12:15:49 2026 -0700 Kevin Moore feat(checks): add prints expectations (`#2742`) (dart-lang/test#2743) 347a5af2 Fri Sep 18 12:09:15 2026 -0700 Nate Bosch Add an example for every matcher (dart-lang/test#2757) 6358dbb3 Thu Sep 17 15:10:08 2026 -0700 Nate Bosch Add an example for every checks expectation, and fix the bugs they exposed (dart-lang/test#2756) 88202adc Thu Sep 17 14:13:30 2026 +0200 Sigurd Meldgaard New flag --shard-by-suite tools (https://github.com/dart-lang/tools/compare/1b0c3bb..3614ce0): 3614ce06 Fri Sep 18 10:07:20 2026 -0700 Kevin Moore chore(unified_analytics): remove dart:mirrors usage in test/event_test.dart (dart-lang/tools#2602) 7550ce13 Fri Sep 18 07:56:44 2026 -0700 Kevin Moore chore(markdown): remove dart:mirrors usage in tool/stats_lib.dart (dart-lang/tools#2600) [email protected] Change-Id: Ide262779269adab780d314d80c9f82b97a79391f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/552741 Commit-Queue: Nate Bosch <[email protected]> Reviewed-by: Brian Quinlan <[email protected]>
When #1958 merged after `http2-v3.1.0` shipped, it replaced the `## 3.1.0` bullet from #1965 (`- Enforce the locally advertised SETTINGS_MAX_CONCURRENT_STREAMS limit on incoming remote streams.`) with the two new bullets. Restore the #1965 bullet so `## 3.1.0` is byte-for-byte identical to the published `3.1.0` tarball on pub.dev.
Move the two `pkgs/http2/CHANGELOG.md` entries added in #1958 (merged 2026-09-21, three days after [`http2-v3.1.0`](https://github.com/dart-lang/http/releases/tag/http2-v3.1.0) was tagged and published on 2026-09-18) from `## 3.1.0` to `## 3.2.0-wip` so they are included in the release notes of the version that ships them.
SETTINGS_MAX_CONCURRENT_STREAMS was only consulted when this endpoint created local streams. A peer could ignore the advertised setting and open unbounded streams, allocating stream state, queues, windows, and controllers.
Enforce the configured peer-initiated stream limit locally before creating Http2StreamImpl. Track active peer streams and locally bound reserved PUSH_PROMISE streams as well. Reject excess streams with RST_STREAM REFUSED_STREAM without publishing them through incomingStreams or peerPushes.
Terminate the connection with GOAWAY ENHANCE_YOUR_CALM after the configured number of consecutive violations. Disabled server push is also enforced on receipt instead of relying on peer compliance or SETTINGS acknowledgement.
Inbound HEADERS and CONTINUATION fragments were accumulated without a size or time bound. Each continuation copied the preceding block again, making fragmented field-block assembly quadratic. HPACK also retained the complete decoded header list before application-level limits could run.
Add the following secure defaults per connection:
Start the field-block timer when the initial HEADERS or PUSH_PROMISE frame header is parsed, before its payload is complete. Retain fragments as chunks and combine them once, enforcing compressed-size and CONTINUATION-count limits while the block is received.
Enforce the decoded field-section limit during HPACK processing using name length + value length + 32 bytes per field. Once the limit is crossed, release retained application-visible headers and continue decoding in discard mode so dynamic-table updates remain synchronized.
Reject a completely decoded oversized field section with RST_STREAM ENHANCE_YOUR_CALM and do not publish its headers. Compressed-size, CONTINUATION-count, and timeout violations terminate the connection with GOAWAY ENHANCE_YOUR_CALM because the shared HPACK context cannot safely continue without complete decompression. Invalid HPACK encoding terminates the connection with COMPRESSION_ERROR.
Advertise SETTINGS_MAX_HEADER_LIST_SIZE while enforcing the configured limit locally because the setting is advisory. This also resolves the unbounded buffering TODO introduced by fdaeafb.
Tests cover ACK-independent stream admission, pre-ACK rejection without abuse counting, post-ACK repeated abuse, exact N and N+1 limits, active and reserved stream recovery, disabled push, repeated abuse, compressed and decoded size boundaries, many continuations, slow initial payloads, Huffman and indexed amplification, HPACK dynamic-table synchronization, HEADERS, trailers, and PUSH_PROMISE.
RFC 9113 sections 4.3, 5.1.2, 6.5.2, and 10.5:
https://www.rfc-editor.org/rfc/rfc9113.html
CERT/CC VU#421644:
https://kb.cert.org/vuls/id/421644