From e1ff7c3cbc2caef1796eeaea2acdd582029ed6f3 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 13 Apr 2018 07:51:35 -0700 Subject: [PATCH] deps: update to nghttp2 1.32.0 This fixes CVE-2018-1000168. PR-URL: https://github.com/nodejs-private/node-private/pull/125 Reviewed-By: Evan Lucas Reviewed-By: Michael Dawson --- deps/nghttp2/lib/CMakeLists.txt | 6 +- deps/nghttp2/lib/includes/nghttp2/nghttp2.h | 10 ++ .../nghttp2/lib/includes/nghttp2/nghttp2ver.h | 4 +- deps/nghttp2/lib/nghttp2_frame.c | 3 + deps/nghttp2/lib/nghttp2_http.c | 2 +- deps/nghttp2/lib/nghttp2_session.c | 151 ++++++++++++------ 6 files changed, 126 insertions(+), 50 deletions(-) diff --git a/deps/nghttp2/lib/CMakeLists.txt b/deps/nghttp2/lib/CMakeLists.txt index 0846d06789a0f1..17e422b22db790 100644 --- a/deps/nghttp2/lib/CMakeLists.txt +++ b/deps/nghttp2/lib/CMakeLists.txt @@ -49,7 +49,7 @@ target_include_directories(nghttp2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/includes" ) -if(HAVE_CUNIT) +if(HAVE_CUNIT OR ENABLE_STATIC_LIB) # Static library (for unittests because of symbol visibility) add_library(nghttp2_static STATIC ${NGHTTP2_SOURCES}) set_target_properties(nghttp2_static PROPERTIES @@ -58,6 +58,10 @@ if(HAVE_CUNIT) ARCHIVE_OUTPUT_NAME nghttp2 ) target_compile_definitions(nghttp2_static PUBLIC "-DNGHTTP2_STATICLIB") + if(ENABLE_STATIC_LIB) + install(TARGETS nghttp2_static + DESTINATION "${CMAKE_INSTALL_LIBDIR}") + endif() endif() install(TARGETS nghttp2 diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h index 13cda9f29e28f5..14f8950bed8d15 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h @@ -3081,6 +3081,16 @@ NGHTTP2_EXTERN int nghttp2_session_set_stream_user_data(nghttp2_session *session, int32_t stream_id, void *stream_user_data); +/** + * @function + * + * Sets |user_data| to |session|, overwriting the existing user data + * specified in `nghttp2_session_client_new()`, or + * `nghttp2_session_server_new()`. + */ +NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session, + void *user_data); + /** * @function * diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h index 455706a5868b3a..d32d2754441b25 100644 --- a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h +++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h @@ -29,7 +29,7 @@ * @macro * Version number of the nghttp2 library release */ -#define NGHTTP2_VERSION "1.29.0" +#define NGHTTP2_VERSION "1.32.0" /** * @macro @@ -37,6 +37,6 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define NGHTTP2_VERSION_NUM 0x011d00 +#define NGHTTP2_VERSION_NUM 0x012000 #endif /* NGHTTP2VER_H */ diff --git a/deps/nghttp2/lib/nghttp2_frame.c b/deps/nghttp2/lib/nghttp2_frame.c index 210df0584443df..fa7cb6953bc539 100644 --- a/deps/nghttp2/lib/nghttp2_frame.c +++ b/deps/nghttp2/lib/nghttp2_frame.c @@ -215,6 +215,9 @@ void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) { nghttp2_ext_altsvc *altsvc; altsvc = frame->payload; + if (altsvc == NULL) { + return; + } /* We use the same buffer for altsvc->origin and altsvc->field_value. */ nghttp2_mem_free(mem, altsvc->origin); diff --git a/deps/nghttp2/lib/nghttp2_http.c b/deps/nghttp2/lib/nghttp2_http.c index 8240f8d76d925d..b08f8863f7ce16 100644 --- a/deps/nghttp2/lib/nghttp2_http.c +++ b/deps/nghttp2/lib/nghttp2_http.c @@ -244,7 +244,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, return NGHTTP2_ERR_HTTP_HEADER; } stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); - if (stream->status_code == -1) { + if (stream->status_code == -1 || stream->status_code == 101) { return NGHTTP2_ERR_HTTP_HEADER; } break; diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c index b14ed77a25c293..a9e7a62390e56a 100644 --- a/deps/nghttp2/lib/nghttp2_session.c +++ b/deps/nghttp2/lib/nghttp2_session.c @@ -219,6 +219,10 @@ static int session_terminate_session(nghttp2_session *session, return 0; } + /* Ignore all incoming frames because we are going to tear down the + session. */ + session->iframe.state = NGHTTP2_IB_IGN_ALL; + if (reason == NULL) { debug_data = NULL; debug_datalen = 0; @@ -2225,8 +2229,9 @@ static int session_prep_frame(nghttp2_session *session, assert(session->obq_flood_counter_ > 0); --session->obq_flood_counter_; } - - if (session_is_closing(session)) { + /* PING frame is allowed to be sent unless termination GOAWAY is + sent */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { return NGHTTP2_ERR_SESSION_CLOSING; } nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); @@ -5345,9 +5350,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) { - - iframe->state = NGHTTP2_IB_IGN_ALL; - rv = session_call_error_callback( session, NGHTTP2_ERR_SETTINGS_EXPECTED, "Remote peer returned unexpected data while we expected " @@ -5394,10 +5396,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length, session->local_settings.max_frame_size); - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); @@ -5405,7 +5403,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } - break; + return (ssize_t)inlen; } switch (iframe->frame.hd.type) { @@ -5419,6 +5417,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, busy = 1; rv = session_on_data_received_fail_fast(session); + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { DEBUGF("recv: DATA not allowed stream_id=%d\n", iframe->frame.hd.stream_id); @@ -5432,7 +5433,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); if (rv < 0) { - iframe->state = NGHTTP2_IB_IGN_DATA; rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "DATA: insufficient padding space"); @@ -5440,7 +5440,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, if (nghttp2_is_fatal(rv)) { return rv; } - break; + return (ssize_t)inlen; } if (rv == 1) { @@ -5461,17 +5461,13 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); if (rv < 0) { - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: insufficient padding space"); if (nghttp2_is_fatal(rv)) { return rv; } - break; + return (ssize_t)inlen; } if (rv == 1) { @@ -5513,6 +5509,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, busy = 1; + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { rv = nghttp2_session_add_rst_stream( session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); @@ -5627,15 +5627,13 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); if (rv < 0) { - busy = 1; - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: insufficient padding space"); if (nghttp2_is_fatal(rv)) { return rv; } - break; + return (ssize_t)inlen; } if (rv == 1) { @@ -5695,11 +5693,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; + return (ssize_t)inlen; default: DEBUGF("recv: extension frame\n"); @@ -5769,6 +5763,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, case NGHTTP2_IB_IGN_PAYLOAD: case NGHTTP2_IB_FRAME_SIZE_ERROR: case NGHTTP2_IB_IGN_DATA: + case NGHTTP2_IB_IGN_ALL: break; default: rv = session_call_on_begin_frame(session, &iframe->frame.hd); @@ -5799,21 +5794,19 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, case NGHTTP2_HEADERS: if (iframe->padlen == 0 && (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); padlen = inbound_frame_compute_pad(iframe); - if (padlen < 0) { - busy = 1; + if (padlen < 0 || + (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding"); if (nghttp2_is_fatal(rv)) { return rv; } - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - break; + return (ssize_t)inlen; } iframe->frame.headers.padlen = (size_t)padlen; - pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); - if (pri_fieldlen > 0) { if (iframe->payloadleft < pri_fieldlen) { busy = 1; @@ -5836,6 +5829,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, busy = 1; + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { rv = nghttp2_session_add_rst_stream( session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); @@ -5860,6 +5857,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + session_inbound_frame_reset(session); break; @@ -5869,6 +5870,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + session_inbound_frame_reset(session); break; @@ -5876,16 +5881,15 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, if (iframe->padlen == 0 && (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { padlen = inbound_frame_compute_pad(iframe); - if (padlen < 0) { - busy = 1; + if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */ + > 1 + iframe->payloadleft) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: invalid padding"); if (nghttp2_is_fatal(rv)) { return rv; } - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - break; + return (ssize_t)inlen; } iframe->frame.push_promise.padlen = (size_t)padlen; @@ -5910,6 +5914,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, busy = 1; + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { rv = nghttp2_session_add_rst_stream( session, iframe->frame.push_promise.promised_stream_id, @@ -5935,6 +5943,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + session_inbound_frame_reset(session); break; @@ -5966,6 +5978,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + session_inbound_frame_reset(session); break; @@ -6027,6 +6043,12 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, data_readlen = inbound_frame_effective_readlen( iframe, iframe->payloadleft - readlen, readlen); + + if (data_readlen == -1) { + /* everything is padding */ + data_readlen = 0; + } + trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && @@ -6046,6 +6068,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + if (rv == NGHTTP2_ERR_PAUSE) { in += hd_proclen; iframe->payloadleft -= hd_proclen; @@ -6155,11 +6181,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + assert(iframe->state == NGHTTP2_IB_IGN_ALL); - break; + return (ssize_t)inlen; case NGHTTP2_IB_READ_SETTINGS: DEBUGF("recv: [IB_READ_SETTINGS]\n"); @@ -6188,6 +6212,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + session_inbound_frame_reset(session); break; @@ -6218,6 +6246,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + session_inbound_frame_reset(session); break; @@ -6257,11 +6289,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; + return (ssize_t)inlen; } /* CONTINUATION won't bear NGHTTP2_PADDED flag */ @@ -6305,6 +6333,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + /* Pad Length field is consumed immediately */ rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); @@ -6313,6 +6345,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); if (stream) { rv = session_update_recv_stream_window_size( @@ -6333,8 +6369,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, if (nghttp2_is_fatal(rv)) { return rv; } - iframe->state = NGHTTP2_IB_IGN_DATA; - break; + return (ssize_t)inlen; } iframe->frame.data.padlen = (size_t)padlen; @@ -6368,6 +6403,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + rv = session_update_recv_stream_window_size( session, stream, readlen, iframe->payloadleft || @@ -6394,6 +6433,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, if (nghttp2_is_fatal(rv)) { return rv; } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } } DEBUGF("recv: data_readlen=%zd\n", data_readlen); @@ -6409,6 +6452,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, if (nghttp2_is_fatal(rv)) { return rv; } + + if (iframe->state == NGHTTP2_IB_IGN_DATA) { + return (ssize_t)inlen; + } } rv = nghttp2_session_add_rst_stream( @@ -6466,6 +6513,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return rv; } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { /* Ignored DATA is considered as "consumed" immediately. */ @@ -6474,6 +6525,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, if (nghttp2_is_fatal(rv)) { return rv; } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } } } @@ -7520,3 +7575,7 @@ size_t nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) { return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater); } + +void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) { + session->user_data = user_data; +}