From 0ce699c7b120ae7c672f4ffd0dc1562db3dae0a7 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 11 Aug 2019 01:31:20 +0200 Subject: [PATCH] http2: stop reading from socket if writes are in progress If a write to the underlying socket finishes asynchronously, that means that we cannot write any more data at that point without waiting for it to finish. If this happens, we should also not be producing any more input. This is part of mitigating CVE-2019-9511/CVE-2019-9517. Backport-PR-URL: https://github.com/nodejs/node/pull/29123 PR-URL: https://github.com/nodejs/node/pull/29122 Reviewed-By: Rich Trott Reviewed-By: James M Snell --- src/node_http2.cc | 17 ++++++++++++++++- src/node_http2.h | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/node_http2.cc b/src/node_http2.cc index c513d286e75d72..e8bf89dd077355 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -1574,9 +1574,18 @@ void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) { void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) { Debug(this, "write finished with status %d", status); + CHECK_NE(flags_ & SESSION_STATE_WRITE_IN_PROGRESS, 0); + flags_ &= ~SESSION_STATE_WRITE_IN_PROGRESS; + // Inform all pending writes about their completion. ClearOutgoing(status); + if ((flags_ & SESSION_STATE_READING_STOPPED) && + nghttp2_session_want_read(session_)) { + flags_ &= ~SESSION_STATE_READING_STOPPED; + stream_->ReadStart(); + } + if (!(flags_ & SESSION_STATE_WRITE_SCHEDULED)) { // Schedule a new write if nghttp2 wants to send data. MaybeScheduleWrite(); @@ -1616,10 +1625,13 @@ void Http2Session::MaybeScheduleWrite() { } void Http2Session::MaybeStopReading() { + if (flags_ & SESSION_STATE_READING_STOPPED) return; int want_read = nghttp2_session_want_read(session_); Debug(this, "wants read? %d", want_read); - if (want_read == 0) + if (want_read == 0 || (flags_ & SESSION_STATE_WRITE_IN_PROGRESS)) { + flags_ |= SESSION_STATE_READING_STOPPED; stream_->ReadStop(); + } } // Unset the sending state, finish up all current writes, and reset @@ -1746,8 +1758,11 @@ uint8_t Http2Session::SendPendingData() { chunks_sent_since_last_write_++; + CHECK_EQ(flags_ & SESSION_STATE_WRITE_IN_PROGRESS, 0); + flags_ |= SESSION_STATE_WRITE_IN_PROGRESS; StreamWriteResult res = underlying_stream()->Write(*bufs, count); if (!res.async) { + flags_ &= ~SESSION_STATE_WRITE_IN_PROGRESS; ClearOutgoing(res.err); } diff --git a/src/node_http2.h b/src/node_http2.h index 6fb8e09751727c..8b393458b05198 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -334,6 +334,8 @@ enum session_state_flags { SESSION_STATE_CLOSED = 0x4, SESSION_STATE_CLOSING = 0x8, SESSION_STATE_SENDING = 0x10, + SESSION_STATE_WRITE_IN_PROGRESS = 0x20, + SESSION_STATE_READING_STOPPED = 0x40, }; typedef uint32_t(*get_setting)(nghttp2_session* session,