Skip to content

Commit f4242e2

Browse files
addaleaxBethGriggs
authored andcommitted
http2: handle 0-length headers better
Ignore headers with 0-length names and track memory for headers the way we track it for other HTTP/2 session memory too. This is intended to mitigate CVE-2019-9516. Backport-PR-URL: #29123 PR-URL: #29122 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 477461a commit f4242e2

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

src/node_http2.cc

+11-2
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,8 @@ void Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) {
13181318
return;
13191319

13201320
std::vector<nghttp2_header> headers(stream->move_headers());
1321+
DecrementCurrentSessionMemory(stream->current_headers_length_);
1322+
stream->current_headers_length_ = 0;
13211323

13221324
Local<String> name_str;
13231325
Local<String> value_str;
@@ -1975,6 +1977,7 @@ Http2Stream::~Http2Stream() {
19751977
if (session_ == nullptr)
19761978
return;
19771979
Debug(this, "tearing down stream");
1980+
session_->DecrementCurrentSessionMemory(current_headers_length_);
19781981
session_->RemoveStream(this);
19791982
session_ = nullptr;
19801983
}
@@ -1989,6 +1992,7 @@ std::string Http2Stream::diagnostic_name() const {
19891992
void Http2Stream::StartHeaders(nghttp2_headers_category category) {
19901993
Debug(this, "starting headers, category: %d", id_, category);
19911994
CHECK(!this->IsDestroyed());
1995+
session_->DecrementCurrentSessionMemory(current_headers_length_);
19921996
current_headers_length_ = 0;
19931997
current_headers_.clear();
19941998
current_headers_category_ = category;
@@ -2260,8 +2264,12 @@ bool Http2Stream::AddHeader(nghttp2_rcbuf* name,
22602264
CHECK(!this->IsDestroyed());
22612265
if (this->statistics_.first_header == 0)
22622266
this->statistics_.first_header = uv_hrtime();
2263-
size_t length = nghttp2_rcbuf_get_buf(name).len +
2264-
nghttp2_rcbuf_get_buf(value).len + 32;
2267+
size_t name_len = nghttp2_rcbuf_get_buf(name).len;
2268+
if (name_len == 0 && !IsReverted(SECURITY_REVERT_CVE_2019_9516)) {
2269+
return true; // Ignore headers with empty names.
2270+
}
2271+
size_t value_len = nghttp2_rcbuf_get_buf(value).len;
2272+
size_t length = name_len + value_len + 32;
22652273
// A header can only be added if we have not exceeded the maximum number
22662274
// of headers and the session has memory available for it.
22672275
if (!session_->IsAvailableSessionMemory(length) ||
@@ -2277,6 +2285,7 @@ bool Http2Stream::AddHeader(nghttp2_rcbuf* name,
22772285
nghttp2_rcbuf_incref(name);
22782286
nghttp2_rcbuf_incref(value);
22792287
current_headers_length_ += length;
2288+
session_->IncrementCurrentSessionMemory(length);
22802289
return true;
22812290
}
22822291

src/node_revert.h

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace node {
1717

1818
#define SECURITY_REVERSIONS(XX) \
1919
XX(CVE_2019_9514, "CVE-2019-9514", "HTTP/2 Reset Flood") \
20+
XX(CVE_2019_9516, "CVE-2019-9516", "HTTP/2 0-Length Headers Leak") \
2021
// XX(CVE_2016_PEND, "CVE-2016-PEND", "Vulnerability Title")
2122
// TODO(addaleax): Remove all of the above before Node.js 13 as the comment
2223
// at the start of the file indicates.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const assert = require('assert');
7+
const http2 = require('http2');
8+
9+
const server = http2.createServer();
10+
server.on('stream', (stream, headers) => {
11+
assert.deepStrictEqual(headers, {
12+
':scheme': 'http',
13+
':authority': `localhost:${server.address().port}`,
14+
':method': 'GET',
15+
':path': '/',
16+
'bar': '',
17+
'__proto__': null
18+
});
19+
stream.session.destroy();
20+
server.close();
21+
});
22+
server.listen(0, common.mustCall(() => {
23+
const client = http2.connect(`http://localhost:${server.address().port}/`);
24+
client.request({ ':path': '/', '': 'foo', 'bar': '' }).end();
25+
}));

0 commit comments

Comments
 (0)