diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3c26a907b6..d676fb51d23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,7 +33,8 @@ release.
-10.4.0
+10.4.1
+10.4.0
10.3.0
10.2.1
10.2.0
diff --git a/configure b/configure
index 654ff458dda..97e4d33e4a0 100755
--- a/configure
+++ b/configure
@@ -637,8 +637,8 @@ def try_check_compiler(cc, lang):
values = (proc.communicate()[0].split() + ['0'] * 7)[0:7]
is_clang = values[0] == '1'
- gcc_version = '%s.%s.%s' % tuple(values[1:1+3])
- clang_version = '%s.%s.%s' % tuple(values[4:4+3])
+ gcc_version = tuple(values[1:1+3])
+ clang_version = tuple(values[4:4+3])
return (True, is_clang, clang_version, gcc_version)
@@ -732,13 +732,13 @@ def check_compiler(o):
ok, is_clang, clang_version, gcc_version = try_check_compiler(CXX, 'c++')
if not ok:
warn('failed to autodetect C++ compiler version (CXX=%s)' % CXX)
- elif clang_version < '3.4.2' if is_clang else gcc_version < '4.9.4':
+ elif clang_version < (3, 4, 2) if is_clang else gcc_version < (4, 9, 4):
warn('C++ compiler too old, need g++ 4.9.4 or clang++ 3.4.2 (CXX=%s)' % CXX)
ok, is_clang, clang_version, gcc_version = try_check_compiler(CC, 'c')
if not ok:
warn('failed to autodetect C compiler version (CC=%s)' % CC)
- elif not is_clang and gcc_version < '4.2.0':
+ elif not is_clang and gcc_version < (4, 2, 0):
# clang 3.2 is a little white lie because any clang version will probably
# do for the C bits. However, we might as well encourage people to upgrade
# to a version that is not completely ancient.
diff --git a/deps/chakrashim/include/v8-version.h b/deps/chakrashim/include/v8-version.h
index f871c8fa676..649deb515aa 100644
--- a/deps/chakrashim/include/v8-version.h
+++ b/deps/chakrashim/include/v8-version.h
@@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 6
#define V8_MINOR_VERSION 7
#define V8_BUILD_NUMBER 288
-#define V8_PATCH_LEVEL 43
+#define V8_PATCH_LEVEL 45
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/deps/nghttp2/lib/CMakeLists.txt b/deps/nghttp2/lib/CMakeLists.txt
index 0846d06789a..17e422b22db 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 13cda9f29e2..14f8950bed8 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 455706a5868..d32d2754441 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 210df058444..fa7cb6953bc 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 8240f8d76d9..b08f8863f7c 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 b14ed77a25c..a9e7a62390e 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;
+}
diff --git a/deps/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py b/deps/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py
index b06bdc4e8b7..da29ebdf1ae 100644
--- a/deps/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py
+++ b/deps/npm/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py
@@ -1262,7 +1262,7 @@ def XcodeVersion():
except:
version = CLTVersion()
if version:
- version = re.match(r'(\d\.\d\.?\d*)', version).groups()[0]
+ version = re.match(r'(\d+\.\d+\.?\d*)', version).groups()[0]
else:
raise GypError("No Xcode or CLT version detected!")
# The CLT has no build information, so we return an empty string.
diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h
index f871c8fa676..649deb515aa 100644
--- a/deps/v8/include/v8-version.h
+++ b/deps/v8/include/v8-version.h
@@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 6
#define V8_MINOR_VERSION 7
#define V8_BUILD_NUMBER 288
-#define V8_PATCH_LEVEL 43
+#define V8_PATCH_LEVEL 45
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h
index ed1a6d4af1a..dcee4eed4e8 100644
--- a/deps/v8/include/v8.h
+++ b/deps/v8/include/v8.h
@@ -9065,6 +9065,7 @@ class Internals {
static const int kNodeStateIsWeakValue = 2;
static const int kNodeStateIsPendingValue = 3;
static const int kNodeStateIsNearDeathValue = 4;
+ static const int kNodeIsIndependentShift = 3;
static const int kNodeIsActiveShift = 4;
static const int kFirstNonstringType = 0x80;
@@ -9288,7 +9289,10 @@ void Persistent::Copy(const Persistent& that) {
template
bool PersistentBase::IsIndependent() const {
- return true;
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return false;
+ return I::GetNodeFlag(reinterpret_cast(this->val_),
+ I::kNodeIsIndependentShift);
}
template
@@ -9377,7 +9381,12 @@ void PersistentBase::RegisterExternalReference(Isolate* isolate) const {
}
template
-void PersistentBase::MarkIndependent() {}
+void PersistentBase::MarkIndependent() {
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return;
+ I::UpdateNodeFlag(reinterpret_cast(this->val_), true,
+ I::kNodeIsIndependentShift);
+}
template
void PersistentBase::MarkActive() {
diff --git a/deps/v8/src/builtins/builtins-date.cc b/deps/v8/src/builtins/builtins-date.cc
index c60275d94ea..73c7098c06b 100644
--- a/deps/v8/src/builtins/builtins-date.cc
+++ b/deps/v8/src/builtins/builtins-date.cc
@@ -166,11 +166,13 @@ void ToDateString(double time_val, Vector str, DateCache* date_cache,
kShortMonths[month], day, year);
return;
case kTimeOnly:
+ // TODO(842085): str may be silently truncated.
SNPrintF(str, "%02d:%02d:%02d GMT%c%02d%02d (%s)", hour, min, sec,
(timezone_offset < 0) ? '-' : '+', timezone_hour, timezone_min,
local_timezone);
return;
case kDateAndTime:
+ // TODO(842085): str may be silently truncated.
SNPrintF(str, "%s %s %02d %04d %02d:%02d:%02d GMT%c%02d%02d (%s)",
kShortWeekDays[weekday], kShortMonths[month], day, year, hour,
min, sec, (timezone_offset < 0) ? '-' : '+', timezone_hour,
diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc
index 90467168ab6..0e9b678ceb7 100644
--- a/deps/v8/src/global-handles.cc
+++ b/deps/v8/src/global-handles.cc
@@ -41,6 +41,8 @@ class GlobalHandles::Node {
STATIC_ASSERT(WEAK == Internals::kNodeStateIsWeakValue);
STATIC_ASSERT(PENDING == Internals::kNodeStateIsPendingValue);
STATIC_ASSERT(NEAR_DEATH == Internals::kNodeStateIsNearDeathValue);
+ STATIC_ASSERT(static_cast(IsIndependent::kShift) ==
+ Internals::kNodeIsIndependentShift);
STATIC_ASSERT(static_cast(IsActive::kShift) ==
Internals::kNodeIsActiveShift);
}
@@ -52,6 +54,7 @@ class GlobalHandles::Node {
object_ = reinterpret_cast |
+10.4.1
10.4.0
10.3.0
10.2.1
@@ -31,6 +32,36 @@
* [io.js](CHANGELOG_IOJS.md)
* [Archive](CHANGELOG_ARCHIVE.md)
+
+## 2018-06-12, Version 10.4.1 (Current), @evanlucas
+
+### Notable Changes
+
+* **Fixes memory exhaustion DoS** (CVE-2018-7164): Fixes a bug introduced in 9.7.0 that increases the memory consumed when reading from the network into JavaScript using the net.Socket object directly as a stream.
+* **http2**
+ * (CVE-2018-7161): Fixes Denial of Service vulnerability by updating the http2 implementation to not crash under certain circumstances during cleanup
+ * (CVE-2018-1000168): Fixes Denial of Service vulnerability by upgrading nghttp2 to 1.32.0
+* **tls** (CVE-2018-7162): Fixes Denial of Service vulnerability by updating the TLS implementation to not crash upon receiving
+* **n-api**: Prevent use-after-free in napi_delete_async_work
+
+### Commits
+
+* [[`1bbfe9a72b`](https://github.com/nodejs/node/commit/1bbfe9a72b)] - **build**: fix configure script for double-digits (Misty De Meo) [#21183](https://github.com/nodejs/node/pull/21183)
+* [[`4c90ee8fc6`](https://github.com/nodejs/node/commit/4c90ee8fc6)] - **deps**: update to nghttp2 1.32.0 (James M Snell) [nodejs-private/node-private#117](https://github.com/nodejs-private/node-private/pull/117)
+* [[`e5c2f575b1`](https://github.com/nodejs/node/commit/e5c2f575b1)] - **deps**: patch V8 to 6.7.288.45 (Michaël Zasso) [#21192](https://github.com/nodejs/node/pull/21192)
+* [[`03ded94ffe`](https://github.com/nodejs/node/commit/03ded94ffe)] - **deps**: patch V8 to 6.7.288.44 (Michaël Zasso) [#21146](https://github.com/nodejs/node/pull/21146)
+* [[`4de7e0c96c`](https://github.com/nodejs/node/commit/4de7e0c96c)] - **deps,npm**: float node-gyp patch on npm (Rich Trott) [#21239](https://github.com/nodejs/node/pull/21239)
+* [[`92d7b6c9a0`](https://github.com/nodejs/node/commit/92d7b6c9a0)] - **fs**: fix promises reads with pos \> 4GB (cjihrig) [#21148](https://github.com/nodejs/node/pull/21148)
+* [[`8681402228`](https://github.com/nodejs/node/commit/8681402228)] - **http2**: fixup http2stream cleanup and other nits (James M Snell) [nodejs-private/node-private#115](https://github.com/nodejs-private/node-private/pull/115)
+* [[`53f8563353`](https://github.com/nodejs/node/commit/53f8563353)] - **n-api**: back up env before async work finalize (Gabriel Schulhof) [#21129](https://github.com/nodejs/node/pull/21129)
+* [[`9ba8ed1371`](https://github.com/nodejs/node/commit/9ba8ed1371)] - **src**: re-add `Realloc()` shrink after reading stream data (Anna Henningsen) [nodejs-private/node-private#128](https://github.com/nodejs-private/node-private/pull/128)
+* [[`8e979482fa`](https://github.com/nodejs/node/commit/8e979482fa)] - ***Revert*** "**src**: restore stdio on program exit" (Evan Lucas) [#21257](https://github.com/nodejs/node/pull/21257)
+* [[`cb5ec64956`](https://github.com/nodejs/node/commit/cb5ec64956)] - **src**: reset TTY mode before cleaning up resources (Anna Henningsen) [#21257](https://github.com/nodejs/node/pull/21257)
+* [[`ae5567eaea`](https://github.com/nodejs/node/commit/ae5567eaea)] - **test**: add regression test for nghttp2 CVE-2018-1000168 (James M Snell) [nodejs-private/node-private#117](https://github.com/nodejs-private/node-private/pull/117)
+* [[`e87bf625dd`](https://github.com/nodejs/node/commit/e87bf625dd)] - **test**: add tls write error regression test (Shigeki Ohtsu) [nodejs-private/node-private#127](https://github.com/nodejs-private/node-private/pull/127)
+* [[`eea2bce58d`](https://github.com/nodejs/node/commit/eea2bce58d)] - **tls**: fix SSL write error handling (Anna Henningsen) [nodejs-private/node-private#127](https://github.com/nodejs-private/node-private/pull/127)
+* [[`1e49eadd68`](https://github.com/nodejs/node/commit/1e49eadd68)] - **tools,gyp**: fix regex for version matching (Rich Trott) [#21216](https://github.com/nodejs/node/pull/21216)
+
## 2018-06-06, Version 10.4.0 (Current), @MylesBorins
diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js
index 4c0a256f5ad..80b30c1a8fa 100644
--- a/lib/internal/fs/promises.js
+++ b/lib/internal/fs/promises.js
@@ -33,7 +33,6 @@ const {
validatePath
} = require('internal/fs/utils');
const {
- isUint32,
validateInt32,
validateUint32
} = require('internal/validators');
@@ -213,7 +212,7 @@ async function read(handle, buffer, offset, length, position) {
validateOffsetLengthRead(offset, length, buffer.length);
- if (!isUint32(position))
+ if (!Number.isSafeInteger(position))
position = -1;
const bytesRead = (await binding.read(handle.fd, buffer, offset, length,
diff --git a/src/node.cc b/src/node.cc
index ef9c2505980..5747c7e5a43 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -103,7 +103,6 @@ typedef int mode_t;
#else
#include
#include // getrlimit, setrlimit
-#include // tcgetattr, tcsetattr
#include // setuid, getuid
#endif
@@ -187,9 +186,6 @@ using v8::Value;
static Mutex process_mutex;
static Mutex environ_mutex;
-// Safe to call more than once and from signal handlers.
-inline void PlatformExit();
-
static bool print_eval = false;
static bool force_repl = false;
static bool syntax_check_only = false;
@@ -1109,7 +1105,7 @@ void AppendExceptionLine(Environment* env,
Mutex::ScopedLock lock(process_mutex);
env->set_printed_error(true);
- PlatformExit();
+ uv_tty_reset_mode();
PrintErrorString("\n%s", arrow);
return;
}
@@ -3074,7 +3070,7 @@ void SetupProcessObject(Environment* env,
void SignalExit(int signo) {
- PlatformExit();
+ uv_tty_reset_mode();
v8_platform.StopTracingAgent();
#ifdef __FreeBSD__
// FreeBSD has a nasty bug, see RegisterSignalHandler for details
@@ -3974,27 +3970,6 @@ static void DebugEnd(const FunctionCallbackInfo& args) {
}
-#ifdef __POSIX__
-static struct {
- int flags;
- bool isatty;
- struct stat stat;
- struct termios termios;
-} stdio[1 + STDERR_FILENO];
-
-
-inline int GetFileDescriptorFlags(int fd) {
- int flags;
-
- do {
- flags = fcntl(fd, F_GETFL);
- } while (flags == -1 && errno == EINTR);
-
- return flags;
-}
-#endif // __POSIX__
-
-
inline void PlatformInit() {
#ifdef __POSIX__
#if HAVE_INSPECTOR
@@ -4005,9 +3980,9 @@ inline void PlatformInit() {
#endif // HAVE_INSPECTOR
// Make sure file descriptors 0-2 are valid before we start logging anything.
- for (auto& s : stdio) {
- const int fd = &s - stdio;
- if (fstat(fd, &s.stat) == 0)
+ for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd += 1) {
+ struct stat ignored;
+ if (fstat(fd, &ignored) == 0)
continue;
// Anything but EBADF means something is seriously wrong. We don't
// have to special-case EINTR, fstat() is not interruptible.
@@ -4015,8 +3990,6 @@ inline void PlatformInit() {
ABORT();
if (fd != open("/dev/null", O_RDWR))
ABORT();
- if (fstat(fd, &s.stat) != 0)
- ABORT();
}
#if HAVE_INSPECTOR
@@ -4039,24 +4012,6 @@ inline void PlatformInit() {
}
#endif // !NODE_SHARED_MODE
- // Record the state of the stdio file descriptors so we can restore it
- // on exit. Needs to happen before installing signal handlers because
- // they make use of that information.
- for (auto& s : stdio) {
- const int fd = &s - stdio;
- int err;
-
- s.flags = GetFileDescriptorFlags(fd);
- CHECK_NE(s.flags, -1);
-
- if (!isatty(fd)) continue;
- s.isatty = true;
- do {
- err = tcgetattr(fd, &s.termios);
- } while (err == -1 && errno == EINTR);
- CHECK_EQ(err, 0);
- }
-
RegisterSignalHandler(SIGINT, SignalExit, true);
RegisterSignalHandler(SIGTERM, SignalExit, true);
@@ -4097,49 +4052,6 @@ inline void PlatformInit() {
}
-// This function must be safe to call more than once and from signal handlers.
-inline void PlatformExit() {
-#ifdef __POSIX__
- for (auto& s : stdio) {
- const int fd = &s - stdio;
-
- struct stat tmp;
- if (-1 == fstat(fd, &tmp)) {
- CHECK_EQ(errno, EBADF); // Program closed file descriptor.
- continue;
- }
-
- bool is_same_file =
- (s.stat.st_dev == tmp.st_dev && s.stat.st_ino == tmp.st_ino);
- if (!is_same_file) continue; // Program reopened file descriptor.
-
- int flags = GetFileDescriptorFlags(fd);
- CHECK_NE(flags, -1);
-
- // Restore the O_NONBLOCK flag if it changed.
- if (O_NONBLOCK & (flags ^ s.flags)) {
- flags &= ~O_NONBLOCK;
- flags |= s.flags & O_NONBLOCK;
-
- int err;
- do {
- err = fcntl(fd, F_SETFL, flags);
- } while (err == -1 && errno == EINTR);
- CHECK_NE(err, -1);
- }
-
- if (s.isatty) {
- int err;
- do {
- err = tcsetattr(fd, TCSANOW, &s.termios);
- } while (err == -1 && errno == EINTR);
- CHECK_NE(err, -1);
- }
- }
-#endif // __POSIX__
-}
-
-
void ProcessArgv(int* argc,
const char** argv,
int* exec_argc,
@@ -4613,6 +4525,7 @@ inline int Start(Isolate* isolate, void* isolate_context,
WaitForInspectorDisconnect(&env);
env.set_can_call_into_js(false);
+ uv_tty_reset_mode();
env.RunCleanup();
RunAtExit(&env);
@@ -4730,7 +4643,7 @@ inline int Start(uv_loop_t* event_loop,
}
int Start(int argc, char** argv) {
- atexit([] () { PlatformExit(); });
+ atexit([] () { uv_tty_reset_mode(); });
PlatformInit();
performance::performance_node_start = PERFORMANCE_NOW();
diff --git a/src/node_api.cc b/src/node_api.cc
index a83244131ff..fdd12afc220 100644
--- a/src/node_api.cc
+++ b/src/node_api.cc
@@ -3393,13 +3393,19 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork {
CallbackScope callback_scope(this);
- NAPI_CALL_INTO_MODULE(_env,
+ // We have to back up the env here because the `NAPI_CALL_INTO_MODULE` macro
+ // makes use of it after the call into the module completes, but the module
+ // may have deallocated **this**, and along with it the place where _env is
+ // stored.
+ napi_env env = _env;
+
+ NAPI_CALL_INTO_MODULE(env,
_complete(_env, ConvertUVErrorCode(status), _data),
- [this] (v8::Local local_err) {
+ [env] (v8::Local local_err) {
// If there was an unhandled exception in the complete callback,
// report it as a fatal exception. (There is no JavaScript on the
// callstack that can possibly handle it.)
- v8impl::trigger_fatal_exception(_env, local_err);
+ v8impl::trigger_fatal_exception(env, local_err);
});
// Note: Don't access `work` after this point because it was
diff --git a/src/node_http2.cc b/src/node_http2.cc
index 2baa52dadf3..361e465d483 100644
--- a/src/node_http2.cc
+++ b/src/node_http2.cc
@@ -506,6 +506,8 @@ Http2Session::Http2Session(Environment* env,
Http2Session::~Http2Session() {
CHECK_EQ(flags_ & SESSION_STATE_HAS_SCOPE, 0);
Debug(this, "freeing nghttp2 session");
+ for (const auto& iter : streams_)
+ iter.second->session_ = nullptr;
nghttp2_session_del(session_);
}
@@ -658,6 +660,8 @@ inline void Http2Session::AddStream(Http2Stream* stream) {
inline void Http2Session::RemoveStream(Http2Stream* stream) {
+ if (streams_.empty() || stream == nullptr)
+ return; // Nothing to remove, item was never added?
streams_.erase(stream->id());
DecrementCurrentSessionMemory(stream->self_size());
}
diff --git a/src/node_http2.h b/src/node_http2.h
index e0a93c8f0fd..ea7a255b35f 100644
--- a/src/node_http2.h
+++ b/src/node_http2.h
@@ -651,8 +651,8 @@ class Http2Stream : public AsyncWrap,
Statistics statistics_ = {};
private:
- Http2Session* session_; // The Parent HTTP/2 Session
- int32_t id_; // The Stream Identifier
+ Http2Session* session_ = nullptr; // The Parent HTTP/2 Session
+ int32_t id_ = 0; // The Stream Identifier
int32_t code_ = NGHTTP2_NO_ERROR; // The RST_STREAM code (if any)
int flags_ = NGHTTP2_STREAM_FLAG_NONE; // Internal state flags
diff --git a/src/node_version.h b/src/node_version.h
index 3d36df6d89a..47ff6d71793 100644
--- a/src/node_version.h
+++ b/src/node_version.h
@@ -24,7 +24,7 @@
#define NODE_MAJOR_VERSION 10
#define NODE_MINOR_VERSION 4
-#define NODE_PATCH_VERSION 0
+#define NODE_PATCH_VERSION 1
#define NODE_VERSION_IS_LTS 0
#define NODE_VERSION_LTS_CODENAME ""
diff --git a/src/stream_base.cc b/src/stream_base.cc
index 71fe5db9758..bb46ea1febb 100644
--- a/src/stream_base.cc
+++ b/src/stream_base.cc
@@ -374,8 +374,9 @@ void EmitToJSStreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) {
}
CHECK_LE(static_cast(nread), buf.len);
+ char* base = Realloc(buf.base, nread);
- Local obj = Buffer::New(env, buf.base, nread).ToLocalChecked();
+ Local obj = Buffer::New(env, base, nread).ToLocalChecked();
stream->CallJSOnreadMethod(nread, obj);
}
@@ -387,6 +388,7 @@ void ReportWritesToJSStreamListener::OnStreamAfterReqFinished(
AsyncWrap* async_wrap = req_wrap->GetAsyncWrap();
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
+ CHECK(!async_wrap->persistent().IsEmpty());
Local req_wrap_obj = async_wrap->object();
Local argv[] = {
diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc
index e74ee02aaa9..ec46088368d 100644
--- a/src/tls_wrap.cc
+++ b/src/tls_wrap.cc
@@ -622,8 +622,10 @@ int TLSWrap::DoWrite(WriteWrap* w,
if (i != count) {
int err;
Local arg = GetSSLError(written, &err, &error_);
- if (!arg.IsEmpty())
+ if (!arg.IsEmpty()) {
+ current_write_ = nullptr;
return UV_EPROTO;
+ }
pending_cleartext_input_.insert(pending_cleartext_input_.end(),
&bufs[i],
diff --git a/test/addons-napi/test_async/test-loop.js b/test/addons-napi/test_async/test-loop.js
new file mode 100644
index 00000000000..efd15bcb2d0
--- /dev/null
+++ b/test/addons-napi/test_async/test-loop.js
@@ -0,0 +1,14 @@
+'use strict';
+const common = require('../../common');
+const assert = require('assert');
+const test_async = require(`./build/${common.buildType}/test_async`);
+const iterations = 500;
+
+let x = 0;
+const workDone = common.mustCall((status) => {
+ assert.strictEqual(status, 0, 'Work completed successfully');
+ if (++x < iterations) {
+ setImmediate(() => test_async.DoRepeatedWork(workDone));
+ }
+}, iterations);
+test_async.DoRepeatedWork(workDone);
diff --git a/test/addons-napi/test_async/test_async.cc b/test/addons-napi/test_async/test_async.cc
index 4d3e025097b..a7ea0eb64c0 100644
--- a/test/addons-napi/test_async/test_async.cc
+++ b/test/addons-napi/test_async/test_async.cc
@@ -1,3 +1,4 @@
+#include
#include
#include "../common.h"
@@ -173,10 +174,52 @@ napi_value TestCancel(napi_env env, napi_callback_info info) {
return nullptr;
}
+struct {
+ napi_ref ref;
+ napi_async_work work;
+} repeated_work_info = { nullptr, nullptr };
+
+static void RepeatedWorkerThread(napi_env env, void* data) {}
+
+static void RepeatedWorkComplete(napi_env env, napi_status status, void* data) {
+ napi_value cb, js_status;
+ NAPI_CALL_RETURN_VOID(env,
+ napi_get_reference_value(env, repeated_work_info.ref, &cb));
+ NAPI_CALL_RETURN_VOID(env,
+ napi_delete_async_work(env, repeated_work_info.work));
+ NAPI_CALL_RETURN_VOID(env,
+ napi_delete_reference(env, repeated_work_info.ref));
+ repeated_work_info.work = nullptr;
+ repeated_work_info.ref = nullptr;
+ NAPI_CALL_RETURN_VOID(env,
+ napi_create_uint32(env, (uint32_t)status, &js_status));
+ NAPI_CALL_RETURN_VOID(env,
+ napi_call_function(env, cb, cb, 1, &js_status, nullptr));
+}
+
+static napi_value DoRepeatedWork(napi_env env, napi_callback_info info) {
+ size_t argc = 1;
+ napi_value cb, name;
+ NAPI_ASSERT(env, repeated_work_info.ref == nullptr,
+ "Reference left over from previous work");
+ NAPI_ASSERT(env, repeated_work_info.work == nullptr,
+ "Work pointer left over from previous work");
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &cb, nullptr, nullptr));
+ NAPI_CALL(env, napi_create_reference(env, cb, 1, &repeated_work_info.ref));
+ NAPI_CALL(env,
+ napi_create_string_utf8(env, "Repeated Work", NAPI_AUTO_LENGTH, &name));
+ NAPI_CALL(env,
+ napi_create_async_work(env, nullptr, name, RepeatedWorkerThread,
+ RepeatedWorkComplete, &repeated_work_info, &repeated_work_info.work));
+ NAPI_CALL(env, napi_queue_async_work(env, repeated_work_info.work));
+ return nullptr;
+}
+
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
DECLARE_NAPI_PROPERTY("Test", Test),
DECLARE_NAPI_PROPERTY("TestCancel", TestCancel),
+ DECLARE_NAPI_PROPERTY("DoRepeatedWork", DoRepeatedWork),
};
NAPI_CALL(env, napi_define_properties(
diff --git a/test/common/http2.js b/test/common/http2.js
index 0f3378e9b80..f84a6686175 100644
--- a/test/common/http2.js
+++ b/test/common/http2.js
@@ -127,8 +127,18 @@ class PingFrame extends Frame {
}
}
+class AltSvcFrame extends Frame {
+ constructor(size) {
+ const buffers = [Buffer.alloc(size)];
+ super(size, 10, 0, 0);
+ buffers.unshift(this[kFrameData]);
+ this[kFrameData] = Buffer.concat(buffers);
+ }
+}
+
module.exports = {
Frame,
+ AltSvcFrame,
DataFrame,
HeadersFrame,
SettingsFrame,
diff --git a/test/common/tls.js b/test/common/tls.js
new file mode 100644
index 00000000000..3560af671bc
--- /dev/null
+++ b/test/common/tls.js
@@ -0,0 +1,176 @@
+/* eslint-disable node-core/required-modules, node-core/crypto-check */
+
+'use strict';
+const crypto = require('crypto');
+const net = require('net');
+
+exports.ccs = Buffer.from('140303000101', 'hex');
+
+class TestTLSSocket extends net.Socket {
+ constructor(server_cert) {
+ super();
+ this.server_cert = server_cert;
+ this.version = Buffer.from('0303', 'hex');
+ this.handshake_list = [];
+ // AES128-GCM-SHA256
+ this.ciphers = Buffer.from('000002009c0', 'hex');
+ this.pre_master_secret =
+ Buffer.concat([this.version, crypto.randomBytes(46)]);
+ this.master_secret = null;
+ this.write_seq = 0;
+ this.client_random = crypto.randomBytes(32);
+
+ this.on('handshake', (msg) => {
+ this.handshake_list.push(msg);
+ });
+
+ this.on('server_random', (server_random) => {
+ this.master_secret = PRF12('sha256', this.pre_master_secret,
+ 'master secret',
+ Buffer.concat([this.client_random,
+ server_random]),
+ 48);
+ const key_block = PRF12('sha256', this.master_secret,
+ 'key expansion',
+ Buffer.concat([server_random,
+ this.client_random]),
+ 40);
+ this.client_writeKey = key_block.slice(0, 16);
+ this.client_writeIV = key_block.slice(32, 36);
+ });
+ }
+
+ createClientHello() {
+ const compressions = Buffer.from('0100', 'hex'); // null
+ const msg = addHandshakeHeader(0x01, Buffer.concat([
+ this.version, this.client_random, this.ciphers, compressions
+ ]));
+ this.emit('handshake', msg);
+ return addRecordHeader(0x16, msg);
+ }
+
+ createClientKeyExchange() {
+ const encrypted_pre_master_secret = crypto.publicEncrypt({
+ key: this.server_cert,
+ padding: crypto.constants.RSA_PKCS1_PADDING
+ }, this.pre_master_secret);
+ const length = Buffer.alloc(2);
+ length.writeUIntBE(encrypted_pre_master_secret.length, 0, 2);
+ const msg = addHandshakeHeader(0x10, Buffer.concat([
+ length, encrypted_pre_master_secret]));
+ this.emit('handshake', msg);
+ return addRecordHeader(0x16, msg);
+ }
+
+ createFinished() {
+ const shasum = crypto.createHash('sha256');
+ shasum.update(Buffer.concat(this.handshake_list));
+ const message_hash = shasum.digest();
+ const r = PRF12('sha256', this.master_secret,
+ 'client finished', message_hash, 12);
+ const msg = addHandshakeHeader(0x14, r);
+ this.emit('handshake', msg);
+ return addRecordHeader(0x16, msg);
+ }
+
+ createIllegalHandshake() {
+ const illegal_handshake = Buffer.alloc(5);
+ return addRecordHeader(0x16, illegal_handshake);
+ }
+
+ parseTLSFrame(buf) {
+ let offset = 0;
+ const record = buf.slice(offset, 5);
+ const type = record[0];
+ const length = record.slice(3, 5).readUInt16BE(0);
+ offset += 5;
+ let remaining = buf.slice(offset, offset + length);
+ if (type === 0x16) {
+ do {
+ remaining = this.parseTLSHandshake(remaining);
+ } while (remaining.length > 0);
+ }
+ offset += length;
+ return buf.slice(offset);
+ }
+
+ parseTLSHandshake(buf) {
+ let offset = 0;
+ const handshake_type = buf[offset];
+ if (handshake_type === 0x02) {
+ const server_random = buf.slice(6, 6 + 32);
+ this.emit('server_random', server_random);
+ }
+ offset += 1;
+ const length = buf.readUIntBE(offset, 3);
+ offset += 3;
+ const handshake = buf.slice(0, offset + length);
+ this.emit('handshake', handshake);
+ offset += length;
+ const remaining = buf.slice(offset);
+ return remaining;
+ }
+
+ encrypt(plain) {
+ const type = plain.slice(0, 1);
+ const version = plain.slice(1, 3);
+ const nonce = crypto.randomBytes(8);
+ const iv = Buffer.concat([this.client_writeIV.slice(0, 4), nonce]);
+ const bob = crypto.createCipheriv('aes-128-gcm', this.client_writeKey, iv);
+ const write_seq = Buffer.alloc(8);
+ write_seq.writeUInt32BE(this.write_seq++, 4);
+ const aad = Buffer.concat([write_seq, plain.slice(0, 5)]);
+ bob.setAAD(aad);
+ const encrypted1 = bob.update(plain.slice(5));
+ const encrypted = Buffer.concat([encrypted1, bob.final()]);
+ const tag = bob.getAuthTag();
+ const length = Buffer.alloc(2);
+ length.writeUInt16BE(nonce.length + encrypted.length + tag.length, 0);
+ return Buffer.concat([type, version, length, nonce, encrypted, tag]);
+ }
+}
+
+function addRecordHeader(type, frame) {
+ const record_layer = Buffer.from('0003030000', 'hex');
+ record_layer[0] = type;
+ record_layer.writeUInt16BE(frame.length, 3);
+ return Buffer.concat([record_layer, frame]);
+}
+
+function addHandshakeHeader(type, msg) {
+ const handshake_header = Buffer.alloc(4);
+ handshake_header[0] = type;
+ handshake_header.writeUIntBE(msg.length, 1, 3);
+ return Buffer.concat([handshake_header, msg]);
+}
+
+function PRF12(algo, secret, label, seed, size) {
+ const newSeed = Buffer.concat([Buffer.from(label, 'utf8'), seed]);
+ return P_hash(algo, secret, newSeed, size);
+}
+
+function P_hash(algo, secret, seed, size) {
+ const result = Buffer.alloc(size);
+ let hmac = crypto.createHmac(algo, secret);
+ hmac.update(seed);
+ let a = hmac.digest();
+ let j = 0;
+ while (j < size) {
+ hmac = crypto.createHmac(algo, secret);
+ hmac.update(a);
+ hmac.update(seed);
+ const b = hmac.digest();
+ let todo = b.length;
+ if (j + todo > size) {
+ todo = size - j;
+ }
+ b.copy(result, j, 0, todo);
+ j += todo;
+ hmac = crypto.createHmac(algo, secret);
+ hmac.update(a);
+ a = hmac.digest();
+ }
+ return result;
+}
+
+exports.TestTLSSocket = TestTLSSocket;
diff --git a/test/parallel/test-fs-promises-file-handle-read.js b/test/parallel/test-fs-promises-file-handle-read.js
index a397b0e260a..621e63c075a 100644
--- a/test/parallel/test-fs-promises-file-handle-read.js
+++ b/test/parallel/test-fs-promises-file-handle-read.js
@@ -8,6 +8,7 @@ const common = require('../common');
const fs = require('fs');
const { open } = fs.promises;
const path = require('path');
+const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const assert = require('assert');
const tmpDir = tmpdir.path;
@@ -40,6 +41,19 @@ async function validateEmptyRead() {
assert.deepStrictEqual(buffer.length, readAsyncHandle.bytesRead);
}
+async function validateLargeRead() {
+ // Reading beyond file length (3 in this case) should return no data.
+ // This is a test for a bug where reads > uint32 would return data
+ // from the current position in the file.
+ const filePath = fixtures.path('x.txt');
+ const fileHandle = await open(filePath, 'r');
+ const pos = 0xffffffff + 1; // max-uint32 + 1
+ const readHandle = await fileHandle.read(Buffer.alloc(1), 0, 1, pos);
+
+ assert.strictEqual(readHandle.bytesRead, 0);
+}
+
validateRead()
.then(validateEmptyRead)
+ .then(validateLargeRead)
.then(common.mustCall());
diff --git a/test/parallel/test-http2-malformed-altsvc.js b/test/parallel/test-http2-malformed-altsvc.js
new file mode 100644
index 00000000000..28c0fb46b42
--- /dev/null
+++ b/test/parallel/test-http2-malformed-altsvc.js
@@ -0,0 +1,39 @@
+'use strict';
+
+const common = require('../common');
+
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const http2 = require('http2');
+const net = require('net');
+const h2test = require('../common/http2');
+
+const server = http2.createServer();
+server.on('stream', common.mustNotCall());
+
+const settings = new h2test.SettingsFrame();
+const settingsAck = new h2test.SettingsFrame(true);
+const altsvc = new h2test.AltSvcFrame((1 << 14) + 1);
+
+server.listen(0, () => {
+ const client = net.connect(server.address().port, () => {
+ client.write(h2test.kClientMagic, () => {
+ client.write(settings.data, () => {
+ client.write(settingsAck.data);
+ // Prior to nghttp2 1.31.1, sending this malformed altsvc frame
+ // would cause a segfault. This test is successful if a segfault
+ // does not occur.
+ client.write(altsvc.data, common.mustCall(() => {
+ client.destroy();
+ }));
+ });
+ });
+ });
+
+ // An error may or may not be emitted on the client side, we don't care
+ // either way if it is, but we don't want to die if it is.
+ client.on('error', () => {});
+ client.on('close', common.mustCall(() => server.close()));
+ client.resume();
+});
diff --git a/test/parallel/test-tls-write-error.js b/test/parallel/test-tls-write-error.js
new file mode 100644
index 00000000000..2783e62d063
--- /dev/null
+++ b/test/parallel/test-tls-write-error.js
@@ -0,0 +1,55 @@
+'use strict';
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const { TestTLSSocket, ccs } = require('../common/tls');
+const fixtures = require('../common/fixtures');
+const https = require('https');
+
+// Regression test for an use-after-free bug in the TLS implementation that
+// would occur when `SSL_write()` failed.
+// Refs: https://github.com/nodejs-private/security/issues/189
+
+const server_key = fixtures.readKey('agent1-key.pem');
+const server_cert = fixtures.readKey('agent1-cert.pem');
+
+const opts = {
+ key: server_key,
+ cert: server_cert
+};
+
+const server = https.createServer(opts, (req, res) => {
+ res.write('hello');
+}).listen(0, common.mustCall(() => {
+ const client = new TestTLSSocket(server_cert);
+
+ client.connect({
+ host: 'localhost',
+ port: server.address().port
+ }, common.mustCall(() => {
+ const ch = client.createClientHello();
+ client.write(ch);
+ }));
+
+ client.once('data', common.mustCall((buf) => {
+ let remaining = buf;
+ do {
+ remaining = client.parseTLSFrame(remaining);
+ } while (remaining.length > 0);
+
+ const cke = client.createClientKeyExchange();
+ const finished = client.createFinished();
+ const ill = client.createIllegalHandshake();
+ const frames = Buffer.concat([
+ cke,
+ ccs,
+ client.encrypt(finished),
+ client.encrypt(ill)
+ ]);
+ client.write(frames, common.mustCall(() => {
+ client.end();
+ server.close();
+ }));
+ }));
+}));
diff --git a/test/sequential/test-net-bytes-per-incoming-chunk-overhead.js b/test/sequential/test-net-bytes-per-incoming-chunk-overhead.js
new file mode 100644
index 00000000000..8f766e8c7a4
--- /dev/null
+++ b/test/sequential/test-net-bytes-per-incoming-chunk-overhead.js
@@ -0,0 +1,41 @@
+// Flags: --expose-gc
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+const net = require('net');
+
+// Tests that, when receiving small chunks, we do not keep the full length
+// of the original allocation for the libuv read call in memory.
+
+let client;
+let baseRSS;
+const receivedChunks = [];
+const N = 250000;
+
+const server = net.createServer(common.mustCall((socket) => {
+ baseRSS = process.memoryUsage().rss;
+
+ socket.setNoDelay(true);
+ socket.on('data', (chunk) => {
+ receivedChunks.push(chunk);
+ if (receivedChunks.length < N) {
+ client.write('a');
+ } else {
+ client.end();
+ server.close();
+ }
+ });
+})).listen(0, common.mustCall(() => {
+ client = net.connect(server.address().port);
+ client.setNoDelay(true);
+ client.write('hello!');
+}));
+
+process.on('exit', () => {
+ global.gc();
+ const bytesPerChunk =
+ (process.memoryUsage().rss - baseRSS) / receivedChunks.length;
+ // We should always have less than one page (usually ~ 4 kB) per chunk.
+ assert(bytesPerChunk < 512, `measured ${bytesPerChunk} bytes per chunk`);
+});
diff --git a/tools/gyp/pylib/gyp/xcode_emulation.py b/tools/gyp/pylib/gyp/xcode_emulation.py
index 667c53695a1..9082b9da358 100644
--- a/tools/gyp/pylib/gyp/xcode_emulation.py
+++ b/tools/gyp/pylib/gyp/xcode_emulation.py
@@ -1403,7 +1403,7 @@ def XcodeVersion():
except:
version = CLTVersion()
if version:
- version = re.match(r'(\d\.\d\.?\d*)', version).groups()[0]
+ version = re.match(r'(\d+\.\d+\.?\d*)', version).groups()[0]
else:
raise GypError("No Xcode or CLT version detected!")
# The CLT has no build information, so we return an empty string.
|