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