Make HttpDecoder::ParsePriorityFrame() more robust.
1. As https://crbug.com/981291 and https://crbug.com/981646 demonstrate,
ParsePriorityFrame() used crashes on certain input. Fix that.
2. Signal QUIC_INVALID_FRAME_DATA instead of QUIC_INTERNAL_ERROR, since
handling invalid input is not an internal bug but an error on the
sender's part.
3. Signal error if PRIORITY frame has superfluous payload.
4. Add tests.
gfe-relnote: n/a, change in QUIC v99-only class
PiperOrigin-RevId: 258409280
Change-Id: If2d5a14c222955ebaa4650b0723be180330c8d1a
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index 1c4c083..3388b2c 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -449,27 +449,42 @@
bool HttpDecoder::ParsePriorityFrame(QuicDataReader* reader,
PriorityFrame* frame) {
uint8_t flags;
- bool success = reader->ReadUInt8(&flags);
- DCHECK(success);
+ if (!reader->ReadUInt8(&flags)) {
+ // TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
+ RaiseError(QUIC_INVALID_FRAME_DATA, "Unable to read PRIORITY frame flags.");
+ return false;
+ }
frame->prioritized_type =
static_cast<PriorityElementType>(ExtractBits(flags, 2, 6));
frame->dependency_type =
static_cast<PriorityElementType>(ExtractBits(flags, 2, 4));
+ // TODO(b/137662729): Update bitmask for exclusive flag.
frame->exclusive = flags % 2 == 1;
- // TODO(bnc): Handle partial delivery.
+ // TODO(b/137359636): Handle partial delivery.
if (frame->prioritized_type != ROOT_OF_TREE &&
!reader->ReadVarInt62(&frame->prioritized_element_id)) {
- RaiseError(QUIC_INTERNAL_ERROR, "Unable to read prioritized_element_id");
+ // TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
+ RaiseError(QUIC_INVALID_FRAME_DATA,
+ "Unable to read prioritized_element_id.");
return false;
}
if (frame->dependency_type != ROOT_OF_TREE &&
!reader->ReadVarInt62(&frame->element_dependency_id)) {
- RaiseError(QUIC_INTERNAL_ERROR, "Unable to read element_dependency_id");
+ // TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
+ RaiseError(QUIC_INVALID_FRAME_DATA,
+ "Unable to read element_dependency_id.");
return false;
}
if (!reader->ReadUInt8(&frame->weight)) {
- RaiseError(QUIC_INTERNAL_ERROR, "Unable to read priority frame weight");
+ // TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
+ RaiseError(QUIC_INVALID_FRAME_DATA,
+ "Unable to read priority frame weight.");
+ return false;
+ }
+ if (!reader->IsDoneReading()) {
+ // TODO(b/124216424): Use HTTP_MALFORMED_FRAME.
+ RaiseError(QUIC_INVALID_FRAME_DATA, "Superfluous data in priority frame.");
return false;
}
return true;
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index 9229db0..735f322 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -411,6 +411,52 @@
EXPECT_EQ("", decoder_.error_detail());
}
+// Regression test for https://crbug.com/981291 and https://crbug.com/981646.
+TEST_F(HttpDecoderTest, CorruptPriorityFrame) {
+ const char* const payload1 =
+ "\x01" // request stream, request stream, exclusive
+ "\x03" // prioritized_element_id
+ "\x04" // element_dependency_id
+ "\xFF" // weight
+ "\xFF"; // superfluous data
+ const char* const payload2 =
+ "\xf1" // root of tree, root of tree, exclusive
+ "\xFF" // weight
+ "\xFF"; // superfluous data
+ struct {
+ const char* const payload;
+ size_t payload_length;
+ const char* const error_message;
+ } kTestData[] = {
+ {payload1, 0, "Unable to read PRIORITY frame flags."},
+ {payload1, 1, "Unable to read prioritized_element_id."},
+ {payload1, 2, "Unable to read element_dependency_id."},
+ {payload1, 3, "Unable to read priority frame weight."},
+ {payload1, 5, "Superfluous data in priority frame."},
+ {payload2, 0, "Unable to read PRIORITY frame flags."},
+ {payload2, 1, "Unable to read priority frame weight."},
+ {payload2, 3, "Superfluous data in priority frame."},
+ };
+
+ for (const auto& test_data : kTestData) {
+ std::string input;
+ input.push_back(2u); // type PRIORITY
+ input.push_back(test_data.payload_length);
+ size_t header_length = input.size();
+ input.append(test_data.payload, test_data.payload_length);
+
+ HttpDecoder decoder(&visitor_);
+ EXPECT_CALL(visitor_, OnPriorityFrameStart(Http3FrameLengths(
+ header_length, test_data.payload_length)));
+
+ QuicByteCount processed_bytes =
+ decoder.ProcessInput(input.data(), input.size());
+ EXPECT_EQ(input.size(), processed_bytes);
+ EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder.error());
+ EXPECT_EQ(test_data.error_message, decoder.error_detail());
+ }
+}
+
TEST_F(HttpDecoderTest, SettingsFrame) {
InSequence s;
std::string input(