Skip to content

Commit 336a98f

Browse files
committed
Implement max settings option
1 parent 3b17a65 commit 336a98f

11 files changed

+124
-0
lines changed

Diff for: doc/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ set(APIDOCS
4242
nghttp2_option_set_no_recv_client_magic.rst
4343
nghttp2_option_set_peer_max_concurrent_streams.rst
4444
nghttp2_option_set_user_recv_extension_type.rst
45+
nghttp2_option_set_max_settings.rst
4546
nghttp2_pack_settings_payload.rst
4647
nghttp2_priority_spec_check_default.rst
4748
nghttp2_priority_spec_default_init.rst

Diff for: doc/Makefile.am

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ APIDOCS= \
6969
nghttp2_option_set_peer_max_concurrent_streams.rst \
7070
nghttp2_option_set_user_recv_extension_type.rst \
7171
nghttp2_option_set_max_outbound_ack.rst \
72+
nghttp2_option_set_max_settings.rst \
7273
nghttp2_pack_settings_payload.rst \
7374
nghttp2_priority_spec_check_default.rst \
7475
nghttp2_priority_spec_default_init.rst \

Diff for: lib/includes/nghttp2/nghttp2.h

+23
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,13 @@ typedef struct {
228228
*/
229229
#define NGHTTP2_CLIENT_MAGIC_LEN 24
230230

231+
/**
232+
* @macro
233+
*
234+
* The default max number of settings per SETTINGS frame
235+
*/
236+
#define NGHTTP2_DEFAULT_MAX_SETTINGS 32
237+
231238
/**
232239
* @enum
233240
*
@@ -398,6 +405,11 @@ typedef enum {
398405
* receives an other type of frame.
399406
*/
400407
NGHTTP2_ERR_SETTINGS_EXPECTED = -536,
408+
/**
409+
* When a local endpoint receives too many settings entries
410+
* in a single SETTINGS frame.
411+
*/
412+
NGHTTP2_ERR_TOO_MANY_SETTINGS = -537,
401413
/**
402414
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
403415
* under unexpected condition and processing was terminated (e.g.,
@@ -2659,6 +2671,17 @@ NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,
26592671
NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option,
26602672
size_t val);
26612673

2674+
/**
2675+
* @function
2676+
*
2677+
* This function sets the maximum number of SETTINGS entries per
2678+
* SETTINGS frame that will be accepted. If more than those entries
2679+
* are received, the peer is considered to be misbehaving and session
2680+
* will be closed. The default value is 32.
2681+
*/
2682+
NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option,
2683+
size_t val);
2684+
26622685
/**
26632686
* @function
26642687
*

Diff for: lib/nghttp2_helper.c

+2
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ const char *nghttp2_strerror(int error_code) {
334334
case NGHTTP2_ERR_FLOODED:
335335
return "Flooding was detected in this HTTP/2 session, and it must be "
336336
"closed";
337+
case NGHTTP2_ERR_TOO_MANY_SETTINGS:
338+
return "SETTINGS frame contained more than the maximum allowed entries";
337339
default:
338340
return "Unknown error code";
339341
}

Diff for: lib/nghttp2_option.c

+5
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,8 @@ void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) {
121121
option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK;
122122
option->max_outbound_ack = val;
123123
}
124+
125+
void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) {
126+
option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS;
127+
option->max_settings = val;
128+
}

Diff for: lib/nghttp2_option.h

+5
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef enum {
6767
NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9,
6868
NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10,
6969
NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11,
70+
NGHTTP2_OPT_MAX_SETTINGS = 1 << 12,
7071
} nghttp2_option_flag;
7172

7273
/**
@@ -85,6 +86,10 @@ struct nghttp2_option {
8586
* NGHTTP2_OPT_MAX_OUTBOUND_ACK
8687
*/
8788
size_t max_outbound_ack;
89+
/**
90+
* NGHTTP2_OPT_MAX_SETTINGS
91+
*/
92+
size_t max_settings;
8893
/**
8994
* Bitwise OR of nghttp2_option_flag to determine that which fields
9095
* are specified.

Diff for: lib/nghttp2_session.c

+21
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ static int session_new(nghttp2_session **session_ptr,
458458

459459
(*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
460460
(*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;
461+
(*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS;
461462

462463
if (option) {
463464
if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
@@ -521,6 +522,11 @@ static int session_new(nghttp2_session **session_ptr,
521522
if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) {
522523
(*session_ptr)->max_outbound_ack = option->max_outbound_ack;
523524
}
525+
526+
if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) &&
527+
option->max_settings) {
528+
(*session_ptr)->max_settings = option->max_settings;
529+
}
524530
}
525531

526532
rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
@@ -5657,6 +5663,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
56575663
iframe->max_niv =
56585664
iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
56595665

5666+
if (iframe->max_niv - 1 > session->max_settings) {
5667+
rv = nghttp2_session_terminate_session_with_reason(
5668+
session, NGHTTP2_ENHANCE_YOUR_CALM,
5669+
"SETTINGS: too many setting entries");
5670+
if (nghttp2_is_fatal(rv)) {
5671+
return rv;
5672+
}
5673+
return (ssize_t)inlen;
5674+
}
5675+
56605676
iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *
56615677
iframe->max_niv);
56625678

@@ -7425,6 +7441,11 @@ static int nghttp2_session_upgrade_internal(nghttp2_session *session,
74257441
if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
74267442
return NGHTTP2_ERR_INVALID_ARGUMENT;
74277443
}
7444+
/* SETTINGS frame contains too many settings */
7445+
if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH
7446+
> session->max_settings) {
7447+
return NGHTTP2_ERR_TOO_MANY_SETTINGS;
7448+
}
74287449
rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,
74297450
settings_payloadlen, mem);
74307451
if (rv != 0) {

Diff for: lib/nghttp2_session.h

+2
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ struct nghttp2_session {
267267
/* The maximum length of header block to send. Calculated by the
268268
same way as nghttp2_hd_deflate_bound() does. */
269269
size_t max_send_header_block_length;
270+
/* The maximum number of settings accepted per SETTINGS frame. */
271+
size_t max_settings;
270272
/* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
271273
uint32_t next_stream_id;
272274
/* The last stream ID this session initiated. For client session,

Diff for: tests/main.c

+2
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ int main() {
317317
test_nghttp2_session_set_local_window_size) ||
318318
!CU_add_test(pSuite, "session_cancel_from_before_frame_send",
319319
test_nghttp2_session_cancel_from_before_frame_send) ||
320+
!CU_add_test(pSuite, "session_too_many_settings",
321+
test_nghttp2_session_too_many_settings) ||
320322
!CU_add_test(pSuite, "session_removed_closed_stream",
321323
test_nghttp2_session_removed_closed_stream) ||
322324
!CU_add_test(pSuite, "session_pause_data",

Diff for: tests/nghttp2_session_test.c

+61
Original file line numberDiff line numberDiff line change
@@ -10614,6 +10614,67 @@ void test_nghttp2_session_cancel_from_before_frame_send(void) {
1061410614
nghttp2_session_del(session);
1061510615
}
1061610616

10617+
void test_nghttp2_session_too_many_settings(void) {
10618+
nghttp2_session *session;
10619+
nghttp2_option *option;
10620+
nghttp2_session_callbacks callbacks;
10621+
nghttp2_frame frame;
10622+
nghttp2_bufs bufs;
10623+
nghttp2_buf *buf;
10624+
ssize_t rv;
10625+
my_user_data ud;
10626+
nghttp2_settings_entry iv[3];
10627+
nghttp2_mem *mem;
10628+
nghttp2_outbound_item *item;
10629+
10630+
mem = nghttp2_mem_default();
10631+
frame_pack_bufs_init(&bufs);
10632+
10633+
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
10634+
callbacks.on_frame_recv_callback = on_frame_recv_callback;
10635+
callbacks.send_callback = null_send_callback;
10636+
10637+
nghttp2_option_new(&option);
10638+
nghttp2_option_set_max_settings(option, 1);
10639+
10640+
nghttp2_session_client_new2(&session, &callbacks, &ud, option);
10641+
10642+
CU_ASSERT(1 == session->max_settings);
10643+
10644+
nghttp2_option_del(option);
10645+
10646+
iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
10647+
iv[0].value = 3000;
10648+
10649+
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
10650+
iv[1].value = 16384;
10651+
10652+
nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2),
10653+
2);
10654+
10655+
rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
10656+
10657+
CU_ASSERT(0 == rv);
10658+
CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
10659+
10660+
nghttp2_frame_settings_free(&frame.settings, mem);
10661+
10662+
buf = &bufs.head->buf;
10663+
assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
10664+
10665+
ud.frame_recv_cb_called = 0;
10666+
10667+
rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
10668+
CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
10669+
10670+
item = nghttp2_session_get_next_ob_item(session);
10671+
CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
10672+
10673+
nghttp2_bufs_reset(&bufs);
10674+
nghttp2_bufs_free(&bufs);
10675+
nghttp2_session_del(session);
10676+
}
10677+
1061710678
static void
1061810679
prepare_session_removed_closed_stream(nghttp2_session *session,
1061910680
nghttp2_hd_deflater *deflater) {

Diff for: tests/nghttp2_session_test.h

+1
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ void test_nghttp2_session_repeated_priority_change(void);
156156
void test_nghttp2_session_repeated_priority_submission(void);
157157
void test_nghttp2_session_set_local_window_size(void);
158158
void test_nghttp2_session_cancel_from_before_frame_send(void);
159+
void test_nghttp2_session_too_many_settings(void);
159160
void test_nghttp2_session_removed_closed_stream(void);
160161
void test_nghttp2_session_pause_data(void);
161162
void test_nghttp2_session_no_closed_streams(void);

0 commit comments

Comments
 (0)