Skip to content

Commit

Permalink
http2: stop reading from socket if writes are in progress
Browse files Browse the repository at this point in the history
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: #29124
PR-URL: #29122
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: James M Snell <[email protected]>
  • Loading branch information
addaleax authored and BethGriggs committed Aug 15, 2019
1 parent a319168 commit 854dba6
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 1 deletion.
19 changes: 18 additions & 1 deletion src/node_http2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1585,9 +1585,18 @@ void Http2Session::OnStreamAfterWriteImpl(WriteWrap* w, int status, void* ctx) {
Http2Session* session = static_cast<Http2Session*>(ctx);
DEBUG_HTTP2SESSION2(session, "write finished with status %d", status);

CHECK_NE(session->flags_ & SESSION_STATE_WRITE_IN_PROGRESS, 0);
session->flags_ &= ~SESSION_STATE_WRITE_IN_PROGRESS;

// Inform all pending writes about their completion.
session->ClearOutgoing(status);

if ((session->flags_ & SESSION_STATE_READING_STOPPED) &&
nghttp2_session_want_read(session->session_)) {
session->flags_ &= ~SESSION_STATE_READING_STOPPED;
session->stream_->ReadStart();
}

if (!(session->flags_ & SESSION_STATE_WRITE_SCHEDULED)) {
// Schedule a new write if nghttp2 wants to send data.
session->MaybeScheduleWrite();
Expand Down Expand Up @@ -1627,10 +1636,13 @@ void Http2Session::MaybeScheduleWrite() {
}

void Http2Session::MaybeStopReading() {
if (flags_ & SESSION_STATE_READING_STOPPED) return;
int want_read = nghttp2_session_want_read(session_);
DEBUG_HTTP2SESSION2(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
Expand Down Expand Up @@ -1757,6 +1769,8 @@ uint8_t Http2Session::SendPendingData() {

chunks_sent_since_last_write_++;

CHECK_EQ(flags_ & SESSION_STATE_WRITE_IN_PROGRESS, 0);

// DoTryWrite may modify both the buffer list start itself and the
// base pointers/length of the individual buffers.
uv_buf_t* writebufs = *bufs;
Expand All @@ -1766,8 +1780,11 @@ uint8_t Http2Session::SendPendingData() {
return 0;
}

flags_ |= SESSION_STATE_WRITE_IN_PROGRESS;

WriteWrap* req = AllocateSend();
if (stream_->DoWrite(req, writebufs, count, nullptr) != 0) {
flags_ &= ~SESSION_STATE_WRITE_IN_PROGRESS;
req->Dispose();
}

Expand Down
2 changes: 2 additions & 0 deletions src/node_http2.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,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,
};

// This allows for 4 default-sized frames with their frame headers
Expand Down

0 comments on commit 854dba6

Please sign in to comment.