1 // Copyright 2016 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef QUICHE_HTTP2_TEST_TOOLS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_ 6 #define QUICHE_HTTP2_TEST_TOOLS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_ 7 8 // Base class for testing concrete payload decoder classes. 9 10 #include <stddef.h> 11 12 #include <string> 13 14 #include "absl/strings/string_view.h" 15 #include "quiche/http2/decoder/decode_buffer.h" 16 #include "quiche/http2/decoder/decode_status.h" 17 #include "quiche/http2/decoder/frame_decoder_state.h" 18 #include "quiche/http2/decoder/http2_frame_decoder_listener.h" 19 #include "quiche/http2/http2_constants.h" 20 #include "quiche/http2/http2_structures.h" 21 #include "quiche/http2/test_tools/frame_parts.h" 22 #include "quiche/http2/test_tools/http2_constants_test_util.h" 23 #include "quiche/http2/test_tools/http2_frame_builder.h" 24 #include "quiche/http2/test_tools/random_decoder_test_base.h" 25 #include "quiche/http2/test_tools/verify_macros.h" 26 #include "quiche/common/platform/api/quiche_export.h" 27 #include "quiche/common/platform/api/quiche_logging.h" 28 #include "quiche/common/quiche_callbacks.h" 29 30 namespace http2 { 31 namespace test { 32 33 // Base class for tests of payload decoders. Below this there is a templated 34 // sub-class that adds a bunch of type specific features. 35 class QUICHE_NO_EXPORT PayloadDecoderBaseTest : public RandomDecoderTest { 36 protected: 37 PayloadDecoderBaseTest(); 38 39 // Virtual functions to be implemented by the test classes for the individual 40 // payload decoders... 41 42 // Start decoding the payload. 43 virtual DecodeStatus StartDecodingPayload(DecodeBuffer* db) = 0; 44 45 // Resume decoding the payload. 46 virtual DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) = 0; 47 48 // In support of ensuring that we're really accessing and updating the 49 // decoder, prepare the decoder by, for example, overwriting the decoder. 50 virtual void PreparePayloadDecoder() = 0; 51 52 // Get the listener to be inserted into the FrameDecoderState, ready for 53 // listening (e.g. reset if it is a FramePartsCollector). 54 virtual Http2FrameDecoderListener* PrepareListener() = 0; 55 56 // Record a frame header for use on each call to StartDecoding. set_frame_header(const Http2FrameHeader & header)57 void set_frame_header(const Http2FrameHeader& header) { 58 EXPECT_EQ(0, InvalidFlagMaskForFrameType(header.type) & header.flags); 59 if (!frame_header_is_set_ || frame_header_ != header) { 60 QUICHE_VLOG(2) << "set_frame_header: " << frame_header_; 61 } 62 frame_header_ = header; 63 frame_header_is_set_ = true; 64 } 65 mutable_state()66 FrameDecoderState* mutable_state() { return frame_decoder_state_.get(); } 67 68 // Randomize the payload decoder, sets the payload decoder's frame_header_, 69 // then start decoding the payload. Called by RandomDecoderTest. This method 70 // is final so that we can always perform certain actions when 71 // RandomDecoderTest starts the decoding of a payload, such as randomizing the 72 // the payload decoder, injecting the frame header and counting fast decoding 73 // cases. Sub-classes must implement StartDecodingPayload to perform their 74 // initial decoding of a frame's payload. 75 DecodeStatus StartDecoding(DecodeBuffer* db) final; 76 77 // Called by RandomDecoderTest. This method is final so that we can always 78 // perform certain actions when RandomDecoderTest calls it, such as counting 79 // slow decode cases. Sub-classes must implement ResumeDecodingPayload to 80 // continue decoding the frame's payload, which must not all be in one buffer. 81 DecodeStatus ResumeDecoding(DecodeBuffer* db) final; 82 83 // Given the specified payload (without the common frame header), decode 84 // it with several partitionings of the payload. 85 ::testing::AssertionResult DecodePayloadAndValidateSeveralWays( 86 absl::string_view payload, Validator validator); 87 88 // TODO(jamessynge): Add helper method for verifying these are both non-zero, 89 // and call the new method from tests that expect successful decoding. ResetDecodeSpeedCounters()90 void ResetDecodeSpeedCounters() { 91 fast_decode_count_ = 0; 92 slow_decode_count_ = 0; 93 } 94 95 // Count of payloads that are full decoded by StartDecodingPayload, or that 96 // an error was detected by StartDecodingPayload. 97 size_t fast_decode_count_ = 0; 98 99 // Count of payloads that require calling ResumeDecodingPayload in order to 100 // decode them completely (or to detect an error during decoding). 101 size_t slow_decode_count_ = 0; 102 103 private: 104 bool frame_header_is_set_ = false; 105 Http2FrameHeader frame_header_; 106 std::unique_ptr<FrameDecoderState> frame_decoder_state_; 107 }; 108 109 // Base class for payload decoders of type Decoder, with corresponding test 110 // peer of type DecoderPeer, and using class Listener as the implementation 111 // of Http2FrameDecoderListenerInterface to be used during decoding. 112 // Typically Listener is a sub-class of FramePartsCollector. 113 // SupportedFrameType is set to false only for UnknownPayloadDecoder. 114 template <class Decoder, class DecoderPeer, class Listener, 115 bool SupportedFrameType = true> 116 class QUICHE_NO_EXPORT AbstractPayloadDecoderTest 117 : public PayloadDecoderBaseTest { 118 protected: 119 // An ApproveSize function returns true to approve decoding the specified 120 // size of payload, else false to skip that size. Typically used for negative 121 // tests; for example, decoding a SETTINGS frame at all sizes except for 122 // multiples of 6. 123 typedef quiche::MultiUseCallback<bool(size_t size)> ApproveSize; 124 AbstractPayloadDecoderTest()125 AbstractPayloadDecoderTest() {} 126 127 // These tests are in setup rather than the constructor for two reasons: 128 // 1) Constructors are not allowed to fail, so gUnit documents that EXPECT_* 129 // and ASSERT_* are not allowed in constructors, and should instead be in 130 // SetUp if they are needed before the body of the test is executed. 131 // 2) To allow the sub-class constructor to make any desired modifications to 132 // the DecoderPeer before these tests are executed; in particular, 133 // UnknownPayloadDecoderPeer has not got a fixed frame type, but it is 134 // instead set during the test's constructor. SetUp()135 void SetUp() override { 136 PayloadDecoderBaseTest::SetUp(); 137 138 // Confirm that DecoderPeer et al returns sensible values. Using auto as the 139 // variable type so that no (narrowing) conversions take place that hide 140 // problems; i.e. if someone changes KnownFlagsMaskForFrameType so that it 141 // doesn't return a uint8, and has bits above the low-order 8 bits set, this 142 // bit of paranoia should detect the problem before we get too far. 143 auto frame_type = DecoderPeer::FrameType(); 144 if (SupportedFrameType) { 145 EXPECT_TRUE(IsSupportedHttp2FrameType(frame_type)) << frame_type; 146 } else { 147 EXPECT_FALSE(IsSupportedHttp2FrameType(frame_type)) << frame_type; 148 } 149 150 auto known_flags = KnownFlagsMaskForFrameType(frame_type); 151 EXPECT_EQ(known_flags, known_flags & 0xff); 152 153 auto flags_to_avoid = DecoderPeer::FlagsAffectingPayloadDecoding(); 154 EXPECT_EQ(flags_to_avoid, flags_to_avoid & known_flags); 155 } 156 PreparePayloadDecoder()157 void PreparePayloadDecoder() override { 158 payload_decoder_ = std::make_unique<Decoder>(); 159 } 160 PrepareListener()161 Http2FrameDecoderListener* PrepareListener() override { 162 listener_.Reset(); 163 return &listener_; 164 } 165 166 // Returns random flags, but only those valid for the frame type, yet not 167 // those that the DecoderPeer says will affect the decoding of the payload 168 // (e.g. the PRIORTY flag on a HEADERS frame or PADDED on DATA frames). RandFlags()169 uint8_t RandFlags() { 170 return Random().Rand8() & 171 KnownFlagsMaskForFrameType(DecoderPeer::FrameType()) & 172 ~DecoderPeer::FlagsAffectingPayloadDecoding(); 173 } 174 175 // Start decoding the payload. StartDecodingPayload(DecodeBuffer * db)176 DecodeStatus StartDecodingPayload(DecodeBuffer* db) override { 177 QUICHE_DVLOG(2) << "StartDecodingPayload, db->Remaining=" 178 << db->Remaining(); 179 return payload_decoder_->StartDecodingPayload(mutable_state(), db); 180 } 181 182 // Resume decoding the payload. ResumeDecodingPayload(DecodeBuffer * db)183 DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) override { 184 QUICHE_DVLOG(2) << "ResumeDecodingPayload, db->Remaining=" 185 << db->Remaining(); 186 return payload_decoder_->ResumeDecodingPayload(mutable_state(), db); 187 } 188 189 // Decode one frame's payload and confirm that the listener recorded the 190 // expected FrameParts instance, and only FrameParts instance. The payload 191 // will be decoded several times with different partitionings of the payload, 192 // and after each the validator will be called. DecodePayloadAndValidateSeveralWays(absl::string_view payload,const FrameParts & expected)193 AssertionResult DecodePayloadAndValidateSeveralWays( 194 absl::string_view payload, const FrameParts& expected) { 195 auto validator = [&expected, this]() -> AssertionResult { 196 HTTP2_VERIFY_FALSE(listener_.IsInProgress()); 197 HTTP2_VERIFY_EQ(1u, listener_.size()); 198 return expected.VerifyEquals(*listener_.frame(0)); 199 }; 200 return PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays( 201 payload, ValidateDoneAndEmpty(validator)); 202 } 203 204 // Decode one frame's payload, expecting that the final status will be 205 // kDecodeError, and that OnFrameSizeError will have been called on the 206 // listener. The payload will be decoded several times with different 207 // partitionings of the payload. The type WrappedValidator is either 208 // RandomDecoderTest::Validator, RandomDecoderTest::NoArgValidator or 209 // std::nullptr_t (not extra validation). 210 template <typename WrappedValidator> VerifyDetectsFrameSizeError(absl::string_view payload,const Http2FrameHeader & header,WrappedValidator wrapped_validator)211 ::testing::AssertionResult VerifyDetectsFrameSizeError( 212 absl::string_view payload, const Http2FrameHeader& header, 213 WrappedValidator wrapped_validator) { 214 set_frame_header(header); 215 // Wrap that validator in another which will check that we've reached 216 // the expected state of kDecodeError with OnFrameSizeError having been 217 // called by the payload decoder. 218 Validator validator = 219 [header, validator = ToValidator(wrapped_validator), this]( 220 const DecodeBuffer& input, 221 DecodeStatus status) -> ::testing::AssertionResult { 222 QUICHE_DVLOG(2) << "VerifyDetectsFrameSizeError validator; status=" 223 << status << "; input.Remaining=" << input.Remaining(); 224 HTTP2_VERIFY_EQ(DecodeStatus::kDecodeError, status); 225 HTTP2_VERIFY_FALSE(listener_.IsInProgress()); 226 HTTP2_VERIFY_EQ(1u, listener_.size()); 227 const FrameParts* frame = listener_.frame(0); 228 HTTP2_VERIFY_EQ(header, frame->GetFrameHeader()); 229 HTTP2_VERIFY_TRUE(frame->GetHasFrameSizeError()); 230 // Verify did not get OnPaddingTooLong, as we should only ever produce 231 // one of these two errors for a single frame. 232 HTTP2_VERIFY_FALSE(frame->GetOptMissingLength()); 233 return validator(input, status); 234 }; 235 return PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays( 236 payload, std::move(validator)); 237 } 238 239 // Confirm that we get OnFrameSizeError when trying to decode unpadded_payload 240 // at all sizes from zero to unpadded_payload.size(), except those sizes not 241 // approved by approve_size. 242 // If total_pad_length is greater than zero, then that amount of padding 243 // is added to the payload (including the Pad Length field). 244 // The flags will be required_flags, PADDED if total_pad_length > 0, and some 245 // randomly selected flag bits not excluded by FlagsAffectingPayloadDecoding. VerifyDetectsMultipleFrameSizeErrors(uint8_t required_flags,absl::string_view unpadded_payload,ApproveSize approve_size,int total_pad_length)246 ::testing::AssertionResult VerifyDetectsMultipleFrameSizeErrors( 247 uint8_t required_flags, absl::string_view unpadded_payload, 248 ApproveSize approve_size, int total_pad_length) { 249 // required_flags should come from those that are defined for the frame 250 // type AND are those that affect the decoding of the payload (otherwise, 251 // the flag shouldn't be required). 252 Http2FrameType frame_type = DecoderPeer::FrameType(); 253 HTTP2_VERIFY_EQ(required_flags, 254 required_flags & KnownFlagsMaskForFrameType(frame_type)); 255 HTTP2_VERIFY_EQ( 256 required_flags, 257 required_flags & DecoderPeer::FlagsAffectingPayloadDecoding()); 258 259 if (0 != 260 (Http2FrameFlag::PADDED & KnownFlagsMaskForFrameType(frame_type))) { 261 // Frame type supports padding. 262 if (total_pad_length == 0) { 263 required_flags &= ~Http2FrameFlag::PADDED; 264 } else { 265 required_flags |= Http2FrameFlag::PADDED; 266 } 267 } else { 268 HTTP2_VERIFY_EQ(0, total_pad_length); 269 } 270 271 bool validated = false; 272 for (size_t real_payload_size = 0; 273 real_payload_size <= unpadded_payload.size(); ++real_payload_size) { 274 if (approve_size != nullptr && !approve_size(real_payload_size)) { 275 continue; 276 } 277 QUICHE_VLOG(1) << "real_payload_size=" << real_payload_size; 278 uint8_t flags = required_flags | RandFlags(); 279 Http2FrameBuilder fb; 280 if (total_pad_length > 0) { 281 // total_pad_length_ includes the size of the Pad Length field, and thus 282 // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255). 283 fb.AppendUInt8(total_pad_length - 1); 284 } 285 // Append a subset of the unpadded_payload, which the decoder should 286 // determine is not a valid amount. 287 fb.Append(unpadded_payload.substr(0, real_payload_size)); 288 if (total_pad_length > 0) { 289 fb.AppendZeroes(total_pad_length - 1); 290 } 291 // We choose a random stream id because the payload decoders aren't 292 // checking stream ids. 293 uint32_t stream_id = RandStreamId(); 294 Http2FrameHeader header(fb.size(), frame_type, flags, stream_id); 295 HTTP2_VERIFY_SUCCESS( 296 VerifyDetectsFrameSizeError(fb.buffer(), header, nullptr)); 297 validated = true; 298 } 299 HTTP2_VERIFY_TRUE(validated); 300 return ::testing::AssertionSuccess(); 301 } 302 303 // As above, but for frames without padding. VerifyDetectsFrameSizeError(uint8_t required_flags,absl::string_view unpadded_payload,ApproveSize approve_size)304 ::testing::AssertionResult VerifyDetectsFrameSizeError( 305 uint8_t required_flags, absl::string_view unpadded_payload, 306 ApproveSize approve_size) { 307 Http2FrameType frame_type = DecoderPeer::FrameType(); 308 uint8_t known_flags = KnownFlagsMaskForFrameType(frame_type); 309 HTTP2_VERIFY_EQ(0, known_flags & Http2FrameFlag::PADDED); 310 HTTP2_VERIFY_EQ(0, required_flags & Http2FrameFlag::PADDED); 311 return VerifyDetectsMultipleFrameSizeErrors( 312 required_flags, unpadded_payload, std::move(approve_size), 0); 313 } 314 315 Listener listener_; 316 std::unique_ptr<Decoder> payload_decoder_; 317 }; 318 319 // A base class for tests parameterized by the total number of bytes of 320 // padding, including the Pad Length field (i.e. a total_pad_length of 0 321 // means unpadded as there is then no room for the Pad Length field). 322 // The frame type must support padding. 323 template <class Decoder, class DecoderPeer, class Listener> 324 class QUICHE_NO_EXPORT AbstractPaddablePayloadDecoderTest 325 : public AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener>, 326 public ::testing::WithParamInterface<int> { 327 typedef AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener> Base; 328 329 protected: 330 using Base::listener_; 331 using Base::Random; 332 using Base::RandStreamId; 333 using Base::set_frame_header; 334 typedef typename Base::Validator Validator; 335 AbstractPaddablePayloadDecoderTest()336 AbstractPaddablePayloadDecoderTest() : total_pad_length_(GetParam()) { 337 QUICHE_LOG(INFO) << "total_pad_length_ = " << total_pad_length_; 338 } 339 340 // Note that total_pad_length_ includes the size of the Pad Length field, 341 // and thus ranges from 0 (no PADDED flag) to 256 (Pad Length == 255). IsPadded()342 bool IsPadded() const { return total_pad_length_ > 0; } 343 344 // Value of the Pad Length field. Only call if IsPadded. pad_length()345 size_t pad_length() const { 346 EXPECT_TRUE(IsPadded()); 347 return total_pad_length_ - 1; 348 } 349 350 // Clear the frame builder and add the Pad Length field if appropriate. Reset()351 void Reset() { 352 frame_builder_ = Http2FrameBuilder(); 353 if (IsPadded()) { 354 frame_builder_.AppendUInt8(pad_length()); 355 } 356 } 357 MaybeAppendTrailingPadding()358 void MaybeAppendTrailingPadding() { 359 if (IsPadded()) { 360 frame_builder_.AppendZeroes(pad_length()); 361 } 362 } 363 RandFlags()364 uint8_t RandFlags() { 365 uint8_t flags = Base::RandFlags(); 366 if (IsPadded()) { 367 flags |= Http2FrameFlag::PADDED; 368 } else { 369 flags &= ~Http2FrameFlag::PADDED; 370 } 371 return flags; 372 } 373 374 // Verify that we get OnPaddingTooLong when decoding payload, and that the 375 // amount of missing padding is as specified. header.IsPadded must be true, 376 // and the payload must be empty or the PadLength field must be too large. VerifyDetectsPaddingTooLong(absl::string_view payload,const Http2FrameHeader & header,size_t expected_missing_length)377 ::testing::AssertionResult VerifyDetectsPaddingTooLong( 378 absl::string_view payload, const Http2FrameHeader& header, 379 size_t expected_missing_length) { 380 set_frame_header(header); 381 auto& listener = listener_; 382 Validator validator = 383 [header, expected_missing_length, &listener]( 384 const DecodeBuffer&, 385 DecodeStatus status) -> ::testing::AssertionResult { 386 HTTP2_VERIFY_EQ(DecodeStatus::kDecodeError, status); 387 HTTP2_VERIFY_FALSE(listener.IsInProgress()); 388 HTTP2_VERIFY_EQ(1u, listener.size()); 389 const FrameParts* frame = listener.frame(0); 390 HTTP2_VERIFY_EQ(header, frame->GetFrameHeader()); 391 HTTP2_VERIFY_TRUE(frame->GetOptMissingLength()); 392 HTTP2_VERIFY_EQ(expected_missing_length, 393 frame->GetOptMissingLength().value()); 394 // Verify did not get OnFrameSizeError. 395 HTTP2_VERIFY_FALSE(frame->GetHasFrameSizeError()); 396 return ::testing::AssertionSuccess(); 397 }; 398 return PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays( 399 payload, std::move(validator)); 400 } 401 402 // Verifies that we get OnPaddingTooLong for a padded frame payload whose 403 // (randomly selected) payload length is less than total_pad_length_. 404 // Flags will be selected at random, except PADDED will be set and 405 // flags_to_avoid will not be set. The stream id is selected at random. VerifyDetectsPaddingTooLong()406 ::testing::AssertionResult VerifyDetectsPaddingTooLong() { 407 uint8_t flags = RandFlags() | Http2FrameFlag::PADDED; 408 409 // Create an all padding payload for total_pad_length_. 410 int payload_length = 0; 411 Http2FrameBuilder fb; 412 if (IsPadded()) { 413 fb.AppendUInt8(pad_length()); 414 fb.AppendZeroes(pad_length()); 415 QUICHE_VLOG(1) << "fb.size=" << fb.size(); 416 // Pick a random length for the payload that is shorter than neccesary. 417 payload_length = Random().Uniform(fb.size()); 418 } 419 420 QUICHE_VLOG(1) << "payload_length=" << payload_length; 421 std::string payload = fb.buffer().substr(0, payload_length); 422 423 // The missing length is the amount we cut off the end, unless 424 // payload_length is zero, in which case the decoder knows only that 1 425 // byte, the Pad Length field, is missing. 426 size_t missing_length = 427 payload_length == 0 ? 1 : fb.size() - payload_length; 428 QUICHE_VLOG(1) << "missing_length=" << missing_length; 429 430 const Http2FrameHeader header(payload_length, DecoderPeer::FrameType(), 431 flags, RandStreamId()); 432 return VerifyDetectsPaddingTooLong(payload, header, missing_length); 433 } 434 435 // total_pad_length_ includes the size of the Pad Length field, and thus 436 // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255). 437 const size_t total_pad_length_; 438 Http2FrameBuilder frame_builder_; 439 }; 440 441 } // namespace test 442 } // namespace http2 443 444 #endif // QUICHE_HTTP2_TEST_TOOLS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_ 445