Initializes the memory backing CallbackVisitor::current_frame_ in the constructor.
With the equivalent change in `//third_party/envoy/external_quiche`, the fuzz test case passes:
```
$ blaze --blazerc=/dev/null test --config=msan-fuzzer --test_strategy=local \
--test_sharding_strategy=disabled --test_env=ENABLE_BLAZE_TEST_FUZZING=1 \
--test_arg=-runs=100 --test_arg=/tmp/testcase-4551436517376000 \
//third_party/envoy/src/test/common/http:http2_codec_impl_fuzz_test
```
http://sponge2/126361b6-06d7-4a4d-a10c-0c21faf44546
PiperOrigin-RevId: 485040699
diff --git a/quiche/http2/adapter/callback_visitor.cc b/quiche/http2/adapter/callback_visitor.cc
index ea782e7..b5e6433 100644
--- a/quiche/http2/adapter/callback_visitor.cc
+++ b/quiche/http2/adapter/callback_visitor.cc
@@ -47,6 +47,7 @@
nghttp2_session_callbacks_new(&c);
*c = callbacks;
callbacks_ = MakeCallbacksPtr(c);
+ memset(¤t_frame_, 0, sizeof(current_frame_));
}
int64_t CallbackVisitor::OnReadyToSend(absl::string_view serialized) {
@@ -78,8 +79,14 @@
<< ", type=" << int(type) << ", length=" << length
<< ", flags=" << int(flags) << ")";
if (static_cast<FrameType>(type) == FrameType::CONTINUATION) {
- // Treat CONTINUATION as HEADERS
- QUICHE_DCHECK_EQ(current_frame_.hd.stream_id, stream_id);
+ if (static_cast<FrameType>(current_frame_.hd.type) != FrameType::HEADERS ||
+ current_frame_.hd.stream_id == 0 ||
+ current_frame_.hd.stream_id != stream_id) {
+ // CONTINUATION frames must follow HEADERS on the same stream. If no
+ // frames have been received, the type is initialized to zero, and the
+ // comparison will fail.
+ return false;
+ }
current_frame_.hd.length += length;
current_frame_.hd.flags |= flags;
QUICHE_DLOG_IF(ERROR, length == 0) << "Empty CONTINUATION!";
diff --git a/quiche/http2/adapter/callback_visitor_test.cc b/quiche/http2/adapter/callback_visitor_test.cc
index 3a42f53..b0bf971 100644
--- a/quiche/http2/adapter/callback_visitor_test.cc
+++ b/quiche/http2/adapter/callback_visitor_test.cc
@@ -199,7 +199,7 @@
// HEADERS on stream 1
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, 0x0)));
- visitor.OnFrameHeader(1, 23, HEADERS, 0x0);
+ ASSERT_TRUE(visitor.OnFrameHeader(1, 23, HEADERS, 0x0));
EXPECT_CALL(callbacks,
OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
@@ -212,7 +212,7 @@
visitor.OnHeaderForStream(1, "server", "my-fake-server");
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, CONTINUATION, 0x4)));
- visitor.OnFrameHeader(1, 23, CONTINUATION, 0x4);
+ ASSERT_TRUE(visitor.OnFrameHeader(1, 23, CONTINUATION, 0x4));
EXPECT_CALL(callbacks,
OnHeader(_, "date", "Tue, 6 Apr 2021 12:54:01 GMT", _));
@@ -225,6 +225,50 @@
visitor.OnEndHeadersForStream(1);
}
+TEST(ClientCallbackVisitorUnitTest, ContinuationNoHeaders) {
+ testing::StrictMock<MockNghttp2Callbacks> callbacks;
+ CallbackVisitor visitor(Perspective::kClient,
+ *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+ // Because no stream precedes the CONTINUATION frame, the stream ID does not
+ // match, and the method returns false.
+ EXPECT_FALSE(visitor.OnFrameHeader(1, 23, CONTINUATION, 0x4));
+}
+
+TEST(ClientCallbackVisitorUnitTest, ContinuationWrongPrecedingType) {
+ testing::StrictMock<MockNghttp2Callbacks> callbacks;
+ CallbackVisitor visitor(Perspective::kClient,
+ *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+
+ EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, WINDOW_UPDATE, _)));
+ visitor.OnFrameHeader(1, 4, WINDOW_UPDATE, 0);
+
+ // Because the CONTINUATION frame does not follow HEADERS, the method returns
+ // false.
+ EXPECT_FALSE(visitor.OnFrameHeader(1, 23, CONTINUATION, 0x4));
+}
+
+TEST(ClientCallbackVisitorUnitTest, ContinuationWrongStream) {
+ testing::StrictMock<MockNghttp2Callbacks> callbacks;
+ CallbackVisitor visitor(Perspective::kClient,
+ *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+ // HEADERS on stream 1
+ EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, 0x0)));
+ ASSERT_TRUE(visitor.OnFrameHeader(1, 23, HEADERS, 0x0));
+
+ EXPECT_CALL(callbacks,
+ OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
+ visitor.OnBeginHeadersForStream(1);
+
+ EXPECT_CALL(callbacks, OnHeader(_, ":status", "200", _));
+ visitor.OnHeaderForStream(1, ":status", "200");
+
+ EXPECT_CALL(callbacks, OnHeader(_, "server", "my-fake-server", _));
+ visitor.OnHeaderForStream(1, "server", "my-fake-server");
+
+ // The CONTINUATION stream ID does not match the one from the HEADERS.
+ EXPECT_FALSE(visitor.OnFrameHeader(3, 23, CONTINUATION, 0x4));
+}
+
TEST(ClientCallbackVisitorUnitTest, ResetAndGoaway) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kClient,