From cf87915c2f27bc20e079ef643f28cff7fc88ab08 Mon Sep 17 00:00:00 2001 From: neeraj netrakar Date: Wed, 14 Oct 2026 00:12:36 +0530 Subject: [PATCH] Fix oversized reads in EncapsulatedSession::InnerStream::Read. Read() reported output.size() as bytes_read even when fewer bytes were buffered on the stream, so a caller reading into a fixed-size buffer would treat the uninitialized tail of its buffer as data received from the peer. The over-sized SkipBytes() call also hit QUICHE_BUG WT_H2_SkipBytes_toomuch and dropped the FIN signal. Count the bytes actually copied, skip only those, and return that count. --- .../encapsulated_web_transport.cc | 5 ++-- .../encapsulated_web_transport_test.cc | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/quiche/web_transport/encapsulated/encapsulated_web_transport.cc b/quiche/web_transport/encapsulated/encapsulated_web_transport.cc index a879870da..a9ee48309 100644 --- a/quiche/web_transport/encapsulated/encapsulated_web_transport.cc +++ b/quiche/web_transport/encapsulated/encapsulated_web_transport.cc @@ -556,8 +556,9 @@ Stream::ReadResult EncapsulatedSession::InnerStream::Read( memcpy(output.data(), read.data.data(), size_to_read); output = output.subspan(size_to_read); } - bool fin_consumed = SkipBytes(total_size); - return ReadResult{total_size, fin_consumed}; + const size_t bytes_read = total_size - output.size(); + bool fin_consumed = SkipBytes(bytes_read); + return ReadResult{bytes_read, fin_consumed}; } Stream::ReadResult EncapsulatedSession::InnerStream::Read(std::string* output) { const size_t total_size = ReadableBytes(); diff --git a/quiche/web_transport/encapsulated/encapsulated_web_transport_test.cc b/quiche/web_transport/encapsulated/encapsulated_web_transport_test.cc index fe9632473..ec3fd831e 100644 --- a/quiche/web_transport/encapsulated/encapsulated_web_transport_test.cc +++ b/quiche/web_transport/encapsulated/encapsulated_web_transport_test.cc @@ -478,6 +478,31 @@ TEST_F(EncapsulatedWebTransportTest, FinRead) { EXPECT_TRUE(read.fin); } +TEST_F(EncapsulatedWebTransportTest, ReadIntoOversizedBuffer) { + std::unique_ptr session = + CreateTransport(Perspective::kClient); + DefaultHandshakeForClient(*session); + EXPECT_CALL(*visitor_, OnIncomingBidirectionalStreamAvailable()); + ProcessIncomingCapsule( + quiche::WebTransportStreamDataCapsule{1, "abcd", false}); + Stream* stream = session->AcceptIncomingBidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + EXPECT_EQ(stream->ReadableBytes(), 4u); + + std::array buffer; + Stream::ReadResult read = stream->Read(absl::MakeSpan(buffer)); + EXPECT_EQ(read.bytes_read, 4u); + EXPECT_FALSE(read.fin); + EXPECT_EQ(absl::string_view(buffer.data(), read.bytes_read), "abcd"); + EXPECT_EQ(stream->ReadableBytes(), 0u); + + ProcessIncomingCapsule(quiche::WebTransportStreamDataCapsule{1, "ef", true}); + read = stream->Read(absl::MakeSpan(buffer)); + EXPECT_EQ(read.bytes_read, 2u); + EXPECT_TRUE(read.fin); + EXPECT_EQ(absl::string_view(buffer.data(), read.bytes_read), "ef"); +} + TEST_F(EncapsulatedWebTransportTest, LargeRead) { std::unique_ptr session = CreateTransport(Perspective::kClient);