Skip to content

Commit d8da265

Browse files
addaleaxtargos
authored andcommitted
http2: treat non-EOF empty frames like other invalid frames
Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38b, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: #37849 PR-URL: #37875 Backport-PR-URL: #38673 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent c3bd0fd commit d8da265

File tree

3 files changed

+44
-1
lines changed

3 files changed

+44
-1
lines changed

src/node_http2.cc

+5-1
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,11 @@ int Http2Session::HandleDataFrame(const nghttp2_frame* frame) {
12771277
frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
12781278
stream->EmitRead(UV_EOF);
12791279
} else if (frame->hd.length == 0) {
1280-
return 1; // Consider 0-length frame without END_STREAM an error.
1280+
if (invalid_frame_count_++ > js_fields_->max_invalid_frames) {
1281+
Debug(this, "rejecting empty-frame-without-END_STREAM flood\n");
1282+
// Consider a flood of 0-length frames without END_STREAM an error.
1283+
return 1;
1284+
}
12811285
}
12821286
return 0;
12831287
}

test/fixtures/emptyframe.http2

4.13 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
const { readSync } = require('../common/fixtures');
6+
const net = require('net');
7+
const http2 = require('http2');
8+
const { once } = require('events');
9+
10+
async function main() {
11+
const blobWithEmptyFrame = readSync('emptyframe.http2');
12+
const server = net.createServer((socket) => {
13+
socket.end(blobWithEmptyFrame);
14+
}).listen(0);
15+
await once(server, 'listening');
16+
17+
for (const maxSessionInvalidFrames of [0, 2]) {
18+
const client = http2.connect(`http://localhost:${server.address().port}`, {
19+
maxSessionInvalidFrames
20+
});
21+
const stream = client.request({
22+
':method': 'GET',
23+
':path': '/'
24+
});
25+
if (maxSessionInvalidFrames) {
26+
stream.on('error', common.mustNotCall());
27+
client.on('error', common.mustNotCall());
28+
} else {
29+
stream.on('error', common.mustCall());
30+
client.on('error', common.mustCall());
31+
}
32+
stream.resume();
33+
await once(stream, 'end');
34+
client.close();
35+
}
36+
server.close();
37+
}
38+
39+
main().then(common.mustCall());

0 commit comments

Comments
 (0)