Skip to content

Commit 3948830

Browse files
jasnelltargos
authored andcommitted
http2: implement support for max settings entries
Adds the maxSettings option to limit the number of settings entries allowed per SETTINGS frame. Default 32 Fixes: https://hackerone.com/reports/446662 CVE-ID: CVE-2020-11080 PR-URL: nodejs-private/node-private#204 Reviewed-By: Matteo Collina <[email protected]>
1 parent d3beb50 commit 3948830

File tree

6 files changed

+70
-3
lines changed

6 files changed

+70
-3
lines changed

doc/api/http2.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,9 @@ value only affects new connections to the server, not any existing connections.
20012001
<!-- YAML
20022002
added: v8.4.0
20032003
changes:
2004+
- version: REPLACEME
2005+
pr-url: https://github.com/nodejs-private/node-private/pull/204
2006+
description: Added `maxSettings` option with a default of 32.
20042007
- version:
20052008
- v13.3.0
20062009
- v12.16.0
@@ -2037,6 +2040,8 @@ changes:
20372040
* `options` {Object}
20382041
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
20392042
for deflating header fields. **Default:** `4Kib`.
2043+
* `maxSettings` {number} Sets the maximum number of settings entries per
2044+
`SETTINGS` frame. The minimum value allowed is `1`. **Default:** `32`.
20402045
* `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session`
20412046
is permitted to use. The value is expressed in terms of number of megabytes,
20422047
e.g. `1` equal 1 megabyte. The minimum value allowed is `1`.
@@ -2132,6 +2137,9 @@ server.listen(80);
21322137
<!-- YAML
21332138
added: v8.4.0
21342139
changes:
2140+
- version: REPLACEME
2141+
pr-url: https://github.com/nodejs-private/node-private/pull/204
2142+
description: Added `maxSettings` option with a default of 32.
21352143
- version:
21362144
- v13.3.0
21372145
- v12.16.0
@@ -2168,6 +2176,8 @@ changes:
21682176
**Default:** `false`.
21692177
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
21702178
for deflating header fields. **Default:** `4Kib`.
2179+
* `maxSettings` {number} Sets the maximum number of settings entries per
2180+
`SETTINGS` frame. The minimum value allowed is `1`. **Default:** `32`.
21712181
* `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session`
21722182
is permitted to use. The value is expressed in terms of number of megabytes,
21732183
e.g. `1` equal 1 megabyte. The minimum value allowed is `1`. This is a
@@ -2250,6 +2260,9 @@ server.listen(80);
22502260
<!-- YAML
22512261
added: v8.4.0
22522262
changes:
2263+
- version: REPLACEME
2264+
pr-url: https://github.com/nodejs-private/node-private/pull/204
2265+
description: Added `maxSettings` option with a default of 32.
22532266
- version: v13.0.0
22542267
pr-url: https://github.com/nodejs/node/pull/29144
22552268
description: The `PADDING_STRATEGY_CALLBACK` has been made equivalent to
@@ -2273,6 +2286,8 @@ changes:
22732286
* `options` {Object}
22742287
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
22752288
for deflating header fields. **Default:** `4Kib`.
2289+
* `maxSettings` {number} Sets the maximum number of settings entries per
2290+
`SETTINGS` frame. The minimum value allowed is `1`. **Default:** `32`.
22762291
* `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session`
22772292
is permitted to use. The value is expressed in terms of number of megabytes,
22782293
e.g. `1` equal 1 megabyte. The minimum value allowed is `1`.

lib/internal/http2/util.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ const IDX_OPTIONS_MAX_HEADER_LIST_PAIRS = 5;
203203
const IDX_OPTIONS_MAX_OUTSTANDING_PINGS = 6;
204204
const IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS = 7;
205205
const IDX_OPTIONS_MAX_SESSION_MEMORY = 8;
206-
const IDX_OPTIONS_FLAGS = 9;
206+
const IDX_OPTIONS_MAX_SETTINGS = 9;
207+
const IDX_OPTIONS_FLAGS = 10;
207208

208209
function updateOptionsBuffer(options) {
209210
let flags = 0;
@@ -252,6 +253,11 @@ function updateOptionsBuffer(options) {
252253
optionsBuffer[IDX_OPTIONS_MAX_SESSION_MEMORY] =
253254
MathMax(1, options.maxSessionMemory);
254255
}
256+
if (typeof options.maxSettings === 'number') {
257+
flags |= (1 << IDX_OPTIONS_MAX_SETTINGS);
258+
optionsBuffer[IDX_OPTIONS_MAX_SETTINGS] =
259+
MathMax(1, options.maxSettings);
260+
}
255261
optionsBuffer[IDX_OPTIONS_FLAGS] = flags;
256262
}
257263

src/node_http2.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,12 @@ Http2Options::Http2Options(Http2State* http2_state, SessionType type) {
193193
// terms of MB increments (i.e. the value 1 == 1 MB)
194194
if (flags & (1 << IDX_OPTIONS_MAX_SESSION_MEMORY))
195195
set_max_session_memory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * 1000000);
196+
197+
if (flags & (1 << IDX_OPTIONS_MAX_SETTINGS)) {
198+
nghttp2_option_set_max_settings(
199+
option,
200+
static_cast<size_t>(buffer[IDX_OPTIONS_MAX_SETTINGS]));
201+
}
196202
}
197203

198204
#define GRABSETTING(entries, count, name) \

src/node_http2_state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ namespace http2 {
5454
IDX_OPTIONS_MAX_OUTSTANDING_PINGS,
5555
IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS,
5656
IDX_OPTIONS_MAX_SESSION_MEMORY,
57+
IDX_OPTIONS_MAX_SETTINGS,
5758
IDX_OPTIONS_FLAGS
5859
};
5960

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const http2 = require('http2');
8+
9+
const server = http2.createServer({ maxSettings: 1 });
10+
11+
// TODO(@jasnell): There is still a session event
12+
// emitted on the server side but it will be destroyed
13+
// immediately after creation and there will be no
14+
// stream created.
15+
server.on('session', common.mustCall((session) => {
16+
session.on('stream', common.mustNotCall());
17+
session.on('remoteSettings', common.mustNotCall());
18+
}));
19+
server.on('stream', common.mustNotCall());
20+
21+
server.listen(0, common.mustCall(() => {
22+
// Specify two settings entries when a max of 1 is allowed.
23+
// Connection should error immediately.
24+
const client = http2.connect(
25+
`http://localhost:${server.address().port}`, {
26+
settings: {
27+
// The actual settings values do not matter.
28+
headerTableSize: 1000,
29+
enablePush: false,
30+
} });
31+
32+
client.on('error', common.mustCall(() => {
33+
server.close();
34+
}));
35+
}));

test/parallel/test-http2-util-update-options-buffer.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ const IDX_OPTIONS_MAX_HEADER_LIST_PAIRS = 5;
2222
const IDX_OPTIONS_MAX_OUTSTANDING_PINGS = 6;
2323
const IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS = 7;
2424
const IDX_OPTIONS_MAX_SESSION_MEMORY = 8;
25-
const IDX_OPTIONS_FLAGS = 9;
25+
const IDX_OPTIONS_MAX_SETTINGS = 9;
26+
const IDX_OPTIONS_FLAGS = 10;
2627

2728
{
2829
updateOptionsBuffer({
@@ -34,7 +35,8 @@ const IDX_OPTIONS_FLAGS = 9;
3435
maxHeaderListPairs: 6,
3536
maxOutstandingPings: 7,
3637
maxOutstandingSettings: 8,
37-
maxSessionMemory: 9
38+
maxSessionMemory: 9,
39+
maxSettings: 10,
3840
});
3941

4042
strictEqual(optionsBuffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE], 1);
@@ -46,6 +48,7 @@ const IDX_OPTIONS_FLAGS = 9;
4648
strictEqual(optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS], 7);
4749
strictEqual(optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS], 8);
4850
strictEqual(optionsBuffer[IDX_OPTIONS_MAX_SESSION_MEMORY], 9);
51+
strictEqual(optionsBuffer[IDX_OPTIONS_MAX_SETTINGS], 10);
4952

5053
const flags = optionsBuffer[IDX_OPTIONS_FLAGS];
5154

@@ -57,6 +60,7 @@ const IDX_OPTIONS_FLAGS = 9;
5760
ok(flags & (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS));
5861
ok(flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_PINGS));
5962
ok(flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS));
63+
ok(flags & (1 << IDX_OPTIONS_MAX_SETTINGS));
6064
}
6165

6266
{

0 commit comments

Comments
 (0)