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