Fix QuicStreamSequencerBuffer::PrefetchNextRegion() behavior after Clear(). If some data are prefetched then QuicStreamSequencerBuffer::Clear() is called, then QuicStreamSequencerBuffer::FirstByteMissing() will take the value of |total_bytes_read_|, which can be less than |total_bytes_prefetched_|. In this case, QuicStreamSequencerBuffer::PrefetchNextRegion() used to return true, but really ought to return false. This CL fixes that. This CL addresses the root cause of the fuzzer-found ASAN crash at https://crbug.com/969391. It is complementary to cr/253592180, which addresses the stream level behavior. Either CLs would be enough to make that particular crash go away, but they are both necessary as they fix different bugs. gfe-relnote: Change in code only mean for QUIC v99, not flag protected. We believe this change does not need flag protection, because it only affects |total_bytes_prefetched_|, which is only read in QuicStreamSequencerBuffer::PrefetchNextRegion(), which is only called (other than tests) in QuicStreamSequencer::PrefetchNextRegion(), which is only called in three places: QuicSpdyStream::OnDataAvailable() but only when using v99, QuicReceiveControlStream::OnDataAvailable() which is not currently wired up but will be v99-only anyway, and //depot/google3/vr/c9/playability/yperf/message_stream.cc in a galaxy far-far away. Therefore this change should not affect production GFE. PiperOrigin-RevId: 253995330 Change-Id: I40b92da16dcf6ec1bdeda9de9ddeeb49ff3542f4
diff --git a/quic/core/quic_stream_sequencer_buffer.cc b/quic/core/quic_stream_sequencer_buffer.cc index 7f0f2cb..9d239b1 100644 --- a/quic/core/quic_stream_sequencer_buffer.cc +++ b/quic/core/quic_stream_sequencer_buffer.cc
@@ -53,6 +53,7 @@ num_bytes_buffered_ = 0; bytes_received_.Clear(); bytes_received_.Add(0, total_bytes_read_); + total_bytes_prefetched_ = total_bytes_read_; } bool QuicStreamSequencerBuffer::RetireBlock(size_t idx) { @@ -336,6 +337,8 @@ bool QuicStreamSequencerBuffer::PrefetchNextRegion(iovec* iov) { DCHECK(iov != nullptr); + DCHECK_LE(total_bytes_read_, total_bytes_prefetched_); + DCHECK_LE(total_bytes_prefetched_, FirstMissingByte()); if (total_bytes_prefetched_ == FirstMissingByte()) { return false;
diff --git a/quic/core/quic_stream_sequencer_buffer_test.cc b/quic/core/quic_stream_sequencer_buffer_test.cc index 335aa2d..67d9b28 100644 --- a/quic/core/quic_stream_sequencer_buffer_test.cc +++ b/quic/core/quic_stream_sequencer_buffer_test.cc
@@ -1078,6 +1078,31 @@ EXPECT_LE(bytes_to_buffer_, total_bytes_written_); } +// Regression test for https://crbug.com/969391. +TEST_F(QuicStreamSequencerBufferTest, PrefetchAfterClear) { + // Write a few bytes. + const std::string kData("foo"); + size_t written = 0; + EXPECT_EQ(QUIC_NO_ERROR, buffer_->OnStreamData(/* offset = */ 0, kData, + &written, &error_details_)); + EXPECT_EQ(kData.size(), written); + + // Prefetch all buffered data. + iovec iov; + EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov)); + EXPECT_EQ(kData, QuicStringPiece(reinterpret_cast<const char*>(iov.iov_base), + iov.iov_len)); + + // No more data to prefetch. + EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov)); + + // Clear all buffered data. + buffer_->Clear(); + + // Still no data to prefetch. + EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov)); +} + } // anonymous namespace } // namespace test