Add validation field for chunk extension validation. PiperOrigin-RevId: 620832541
diff --git a/quiche/balsa/balsa_enums.cc b/quiche/balsa/balsa_enums.cc index bc6b68f..fa072fc 100644 --- a/quiche/balsa/balsa_enums.cc +++ b/quiche/balsa/balsa_enums.cc
@@ -76,6 +76,8 @@ return "INVALID_CHUNK_LENGTH"; case CHUNK_LENGTH_OVERFLOW: return "CHUNK_LENGTH_OVERFLOW"; + case INVALID_CHUNK_EXTENSION: + return "INVALID_CHUNK_EXTENSION"; case CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO: return "CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO"; case CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT:
diff --git a/quiche/balsa/balsa_enums.h b/quiche/balsa/balsa_enums.h index 60537a3..61c487e 100644 --- a/quiche/balsa/balsa_enums.h +++ b/quiche/balsa/balsa_enums.h
@@ -81,6 +81,7 @@ // Chunking errors INVALID_CHUNK_LENGTH, CHUNK_LENGTH_OVERFLOW, + INVALID_CHUNK_EXTENSION, // Other errors. CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO,
diff --git a/quiche/balsa/balsa_frame.cc b/quiche/balsa/balsa_frame.cc index 03bc994..1e6080f 100644 --- a/quiche/balsa/balsa_frame.cc +++ b/quiche/balsa/balsa_frame.cc
@@ -1129,6 +1129,12 @@ return current - input; } const char c = *current; + if (http_validation_policy_.disallow_lone_cr_in_chunk_extension && + c == '\r' && (current + 1 == end || *(current + 1) != '\n')) { + // We have a lone carriage return. + HandleError(BalsaFrameEnums::INVALID_CHUNK_EXTENSION); + return current - input; + } if (c == '\r' || c == '\n') { extensions_length = (extensions_start == current) ? 0
diff --git a/quiche/balsa/balsa_frame_test.cc b/quiche/balsa/balsa_frame_test.cc index 294e11f..e495bc0 100644 --- a/quiche/balsa/balsa_frame_test.cc +++ b/quiche/balsa/balsa_frame_test.cc
@@ -1667,6 +1667,29 @@ EXPECT_EQ(BalsaFrameEnums::BALSA_NO_ERROR, balsa_frame_.ErrorCode()); } +TEST_F(HTTPBalsaFrameTest, InvalidChunkExtensionWithCarriageReturn) { + balsa_frame_.set_http_validation_policy( + HttpValidationPolicy{.disallow_lone_cr_in_chunk_extension = true}); + std::string message_headers = + "POST /potato?salad=withmayo HTTP/1.1\r\n" + "transfer-encoding: chunked\r\n" + "\r\n"; + std::string message_body = + "9; bad\rextension\r\n" + "012345678\r\n" + "0\r\n" + "\r\n"; + std::string message = + std::string(message_headers) + std::string(message_body); + + EXPECT_CALL(visitor_mock_, + HandleError(BalsaFrameEnums::INVALID_CHUNK_EXTENSION)); + ASSERT_EQ(message_headers.size(), + balsa_frame_.ProcessInput(message.data(), message.size())); + balsa_frame_.ProcessInput(message.data() + message_headers.size(), + message.size()); +} + TEST_F(HTTPBalsaFrameTest, VisitorInvokedProperlyForRequestWithTransferEncoding) { std::string message_headers =
diff --git a/quiche/balsa/http_validation_policy.h b/quiche/balsa/http_validation_policy.h index 9ea158ec..b35799a 100644 --- a/quiche/balsa/http_validation_policy.h +++ b/quiche/balsa/http_validation_policy.h
@@ -59,6 +59,10 @@ // neither, depending on InvalidCharsLevel, if a request header value contains // a carriage return that is not succeeded by a line feed. bool disallow_lone_cr_in_request_headers = false; + + // The RFC is quite specific about chunk extensions formatting, but we only + // verify that there are no CR without a subsequent LF. + bool disallow_lone_cr_in_chunk_extension = false; }; } // namespace quiche