HTTP/2 CONTINUATION Flood Vulnerability
What the report claims
The report claims that libcurl's HTTP/2 CONTINUATION frame handling lacks limits on the number of frames and cumulative header size, leading to potential OOM crashes or remote code execution.
Compare with curl's recorded outcome
Labeled AI slop
curl's maintainer lists this report as AI slop
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The available evidence does not settle the report. A maintainer should investigate.
Why this finding?
- The report's core code snippet (Curl_add_buffer_init, Curl_add_buffer) is contradicted by deterministic checks — these functions do not exist at the target revision e1f87a093b71.
- The actual lib/http2.c implementation is not provided in the source material, so the claims about missing CONTINUATION frame limits and cumulative size checks cannot be verified against real code.
- The RFC 7540 claim about a <=10 CONTINUATION frame limit is contradicted by the RFC text which states 'Any number of CONTINUATION frames can be sent'.
- The vulnerability class (unbounded header fragment accumulation) is plausible for HTTP/2 implementations, but the provided evidence does not ground it in curl's actual code at this revision.
What still needs checking
- The complete lib/http2.c source at revision e1f87a093b71, specifically the CONTINUATION frame handling logic and header buffer management.
How this analysis ran 6 claims · 0 citations verified
Checked against curl master on 2025-05-04 (e1f87a093b71). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Review a draft reply to the reporter
Edit and verify before sending.
Thank you for the detailed report. The code snippet you reference (Curl_add_buffer_init, Curl_add_buffer) does not exist in curl's master branch at the revision you target (e1f87a093b71). Please provide the exact function names and file locations in the current codebase where CONTINUATION frames are processed and header fragments are accumulated, so we can verify the missing limits you describe.
Inspect all claims and source code 6 claims · 2 code excerpts
Report claims
No upper limit on the number of CONTINUATION frames
Read the reasoning
The report's code snippet shows a while loop with Curl_add_buffer_init and Curl_add_buffer, but deterministic checks confirm these symbols do not exist at the target revision. The actual lib/http2.c implementation is not provided, so the claim about missing CONTINUATION frame count limit cannot be verified against the real code.
No cumulative header block size check
Read the reasoning
The cumulative header block size check claim depends on the actual http2.c implementation which is not provided. The report's snippet is not grounded in the target revision's code.
Uncontrolled memory allocation leading to OOM crashes or potential remote code execution
Read the reasoning
Without the actual http2.c code, the impact of uncontrolled memory allocation cannot be assessed. The report's PoC assumes the flawed snippet is real, but deterministic checks show the snippet's functions don't exist at this revision.
Trigger uncontrolled memory allocation by sending 65535 CONTINUATION frames with 16384-byte data payloads
Read the reasoning
The reproduction steps assume the vulnerable code path exists. Since the report's code snippet is contradicted by deterministic checks (Curl_add_buffer_init/buffer absent), the reproduction cannot be validated against the actual implementation.
Send CONTINUATION frames sequentially within the same stream without size validation
Read the reasoning
The trigger description matches the report's fabricated snippet, not the verified codebase. NGHTTP2_CONTINUATION constant may exist but the handling logic is unknown.
Memory usage exceeds 10GB within seconds causing client crash
Read the reasoning
The 10GB memory claim is an extrapolation from the report's assumed vulnerability. Without the actual buffer management code, this cannot be confirmed or denied.
Deterministic checks that failed
- symbol Curl_add_buffer_init exists in other curl releases but not at e1f87a093b71
- symbol Curl_add_buffer exists in other curl releases but not at e1f87a093b71
- quote only 0/5 quoted lines appear anywhere in curl at e1f87a093b71
What the report says about other projects
contradicted RFC 7540 suggests <= 10 CONTINUATION frames per header block
“Any number of CONTINUATION frames can be sent on an existing stream, as long as the preceding frame is on the same stream and is a HEADERS, PUSH_PROMISE or CONTINUATION frame without the END_HEADERS flag set.” source
confirmed Single-frame header block size limit of 16KB is enforced
“The maximum frame payload size is 2^14-1 (16,383) octets. An endpoint MUST treat the receipt of a frame with a larger payload as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR.” source
Read the original report (excerpt)
Source code
| 74 | while(<STDIN>) { |
| 75 | my $l = $_; |
| 76 | if($fl) { |
| 77 | # continued deprecation |
| 78 | if($l =~ /(.*)\),/) { |
| 79 | $fl .= $1; |
| 80 | |
| 81 | # the end |
| 82 | my @p=split(/, */, $fl); |
| 83 | add($p[0], $p[1], $p[2]); |
| 84 | undef $fl; |
| 85 | } |
| 86 | else { |
| 87 | # another line to append |
| 88 | chomp $l; |
| 89 | $fl .= $l; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | if(/^ *CURLOPTDEPRECATED\((.*)/) { |
| 94 | $fl = $1; |
| 95 | chomp $fl; |
| 96 | } |
| 97 | |
| 98 | if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) { |
| 99 | my($opt, $type, $num)=($1,$2,$3); |
| 100 | add($opt, $type, $num); |
| 101 | } |
| 102 | |
| 103 | # alias for an older option |
| 104 | # old = new |
| 105 | if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) { |
| 106 | my ($o, $n)=($1, $2); |
| 107 | # skip obsolete ones |
| 108 | if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) { |
| 109 | $o =~ s/^CURLOPT_//; |
| 110 | $n =~ s/^CURLOPT_//; |
| 111 | $alias{$o} = $n; |
| 112 | push @names, $o; |
| 113 | } |
| 114 | } |
| 115 | } |
| 74 | while(<STDIN>) { |
| 75 | my $l = $_; |
| 76 | if($fl) { |
| 77 | # continued deprecation |
| 78 | if($l =~ /(.*)\),/) { |
| 79 | $fl .= $1; |
| 80 | |
| 81 | # the end |
| 82 | my @p=split(/, */, $fl); |
| 83 | add($p[0], $p[1], $p[2]); |
| 84 | undef $fl; |
| 85 | } |
| 86 | else { |
| 87 | # another line to append |
| 88 | chomp $l; |
| 89 | $fl .= $l; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | if(/^ *CURLOPTDEPRECATED\((.*)/) { |
| 94 | $fl = $1; |
| 95 | chomp $fl; |
| 96 | } |
| 97 | |
| 98 | if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) { |
| 99 | my($opt, $type, $num)=($1,$2,$3); |
| 100 | add($opt, $type, $num); |
| 101 | } |
| 102 | |
| 103 | # alias for an older option |
| 104 | # old = new |
| 105 | if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) { |
| 106 | my ($o, $n)=($1, $2); |
| 107 | # skip obsolete ones |
| 108 | if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) { |
| 109 | $o =~ s/^CURLOPT_//; |
| 110 | $n =~ s/^CURLOPT_//; |
| 111 | $alias{$o} = $n; |
| 112 | push @names, $o, |
| 113 | } |
| 114 | } |
| 115 | } |