Do not call HttpDecoder::ProcessInput after error. This CL changes API contract and call sites. Without the change to the call sites, //gfe/gfe2/test_tools/fuzzing:gfe_quic_fuzzer would crash on the new DCHECK_NE(QUIC_NO_ERROR, error_). This is because even if Visitor::OnError() closes the connection, the sequencer might have more data that QuicSpdyStream is eager to read and feed to HttpDecoder::ProcessInput(). Currently HttpDecoder::ProcessInput() ignores input upon error. However, I'm going to modify the call sites to use PeekRegion() instead of PrefetchNextRegion() and spin until decoding is paused by a visitor method or no new region is there to peek at. Since HttpDecoder::ProcessInput() returns 0 if called after an error, this would just result in PeekRegion() being called with the same offset, returning true, and the cycle spinning in an infinite loop. gfe-relnote: n/a, change to QUIC v99-only code path. PiperOrigin-RevId: 255168809 Change-Id: I6b6f0b0986f2f6bec0a8eedb695dc91943d57bf2
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc index 1bcbfaa..ffd5c74 100644 --- a/quic/core/http/http_decoder.cc +++ b/quic/core/http/http_decoder.cc
@@ -45,10 +45,17 @@ HttpDecoder::~HttpDecoder() {} QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) { + DCHECK_EQ(QUIC_NO_ERROR, error_); + DCHECK_NE(STATE_ERROR, state_); + QuicDataReader reader(data, len); bool continue_processing = true; - while (continue_processing && error_ == QUIC_NO_ERROR && + while (continue_processing && (reader.BytesRemaining() != 0 || state_ == STATE_FINISH_PARSING)) { + // |continue_processing| must have been set to false upon error. + DCHECK_EQ(QUIC_NO_ERROR, error_); + DCHECK_NE(STATE_ERROR, state_); + switch (state_) { case STATE_READING_FRAME_TYPE: ReadFrameType(&reader);
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h index fa0a27b..515daae 100644 --- a/quic/core/http/http_decoder.h +++ b/quic/core/http/http_decoder.h
@@ -124,10 +124,14 @@ // visitor method returns false or an error occurs. Returns the number of // bytes processed. Does not process any input if called after an error. // Paused processing can be resumed by calling ProcessInput() again with the - // unprocessed portion of data. + // unprocessed portion of data. Must not be called after an error has + // occurred. QuicByteCount ProcessInput(const char* data, QuicByteCount len); + // Returns an error code other than QUIC_NO_ERROR if and only if + // Visitor::OnError() has been called. QuicErrorCode error() const { return error_; } + const std::string& error_detail() const { return error_detail_; } private:
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc index e7efad1..9e166e0 100644 --- a/quic/core/http/quic_receive_control_stream.cc +++ b/quic/core/http/quic_receive_control_stream.cc
@@ -141,7 +141,8 @@ void QuicReceiveControlStream::OnDataAvailable() { iovec iov; - while (!reading_stopped() && sequencer()->PrefetchNextRegion(&iov)) { + while (!reading_stopped() && decoder_.error() == QUIC_NO_ERROR && + sequencer()->PrefetchNextRegion(&iov)) { decoder_.ProcessInput(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len); }
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc index 92138a4..b7522c0 100644 --- a/quic/core/http/quic_spdy_stream.cc +++ b/quic/core/http/quic_spdy_stream.cc
@@ -623,7 +623,8 @@ } iovec iov; - while (!reading_stopped() && sequencer()->PrefetchNextRegion(&iov)) { + while (!reading_stopped() && decoder_.error() == QUIC_NO_ERROR && + sequencer()->PrefetchNextRegion(&iov)) { DCHECK(!sequencer()->IsClosed()); decoder_.ProcessInput(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len);