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(