Skip to content

Commit 55e4c72

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 <matteo.collina@gmail.com>
1 parent 0a7bf50 commit 55e4c72

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
@@ -1991,6 +1991,9 @@ value only affects new connections to the server, not any existing connections.
19911991
<!-- YAML
19921992
added: v8.4.0
19931993
changes:
1994+
- version: REPLACEME
1995+
pr-url: https://github.com/nodejs-private/node-private/pull/204
1996+
description: Added `maxSettings` option with a default of 32.
19941997
- version:
19951998
- v13.3.0
19961999
- v12.16.0
@@ -2027,6 +2030,8 @@ changes:
20272030
* `options` {Object}
20282031
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
20292032
for deflating header fields. **Default:** `4Kib`.
2033+
* `maxSettings` {number} Sets the maximum number of settings entries per
2034+
`SETTINGS` frame. The minimum value allowed is `1`. **Default:** `32`.
20302035
* `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session`
20312036
is permitted to use. The value is expressed in terms of number of megabytes,
20322037
e.g. `1` equal 1 megabyte. The minimum value allowed is `1`.
@@ -2122,6 +2127,9 @@ server.listen(80);
21222127
<!-- YAML
21232128
added: v8.4.0
21242129
changes:
2130+
- version: REPLACEME
2131+
pr-url: https://github.com/nodejs-private/node-private/pull/204
2132+
description: Added `maxSettings` option with a default of 32.
21252133
- version:
21262134
- v13.3.0
21272135
- v12.16.0
@@ -2158,6 +2166,8 @@ changes:
21582166
**Default:** `false`.
21592167
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
21602168
for deflating header fields. **Default:** `4Kib`.
2169+
* `maxSettings` {number} Sets the maximum number of settings entries per
2170+
`SETTINGS` frame. The minimum value allowed is `1`. **Default:** `32`.
21612171
* `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session`
21622172
is permitted to use. The value is expressed in terms of number of megabytes,
21632173
e.g. `1` equal 1 megabyte. The minimum value allowed is `1`. This is a
@@ -2240,6 +2250,9 @@ server.listen(80);
22402250
<!-- YAML
22412251
added: v8.4.0
22422252
changes:
2253+
- version: REPLACEME
2254+
pr-url: https://github.com/nodejs-private/node-private/pull/204
2255+
description: Added `maxSettings` option with a default of 32.
22432256
- version: v13.0.0
22442257
pr-url: https://github.com/nodejs/node/pull/29144
22452258
description: The `PADDING_STRATEGY_CALLBACK` has been made equivalent to
@@ -2263,6 +2276,8 @@ changes:
22632276
* `options` {Object}
22642277
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
22652278
for deflating header fields. **Default:** `4Kib`.
2279+
* `maxSettings` {number} Sets the maximum number of settings entries per
2280+
`SETTINGS` frame. The minimum value allowed is `1`. **Default:** `32`.
22662281
* `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session`
22672282
is permitted to use. The value is expressed in terms of number of megabytes,
22682283
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
@@ -195,6 +195,12 @@ Http2Options::Http2Options(Http2State* http2_state, SessionType type) {
195195
// terms of MB increments (i.e. the value 1 == 1 MB)
196196
if (flags & (1 << IDX_OPTIONS_MAX_SESSION_MEMORY))
197197
set_max_session_memory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * 1000000);
198+
199+
if (flags & (1 << IDX_OPTIONS_MAX_SETTINGS)) {
200+
nghttp2_option_set_max_settings(
201+
option,
202+
static_cast<size_t>(buffer[IDX_OPTIONS_MAX_SETTINGS]));
203+
}
198204
}
199205

200206
#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)