1 #include "quiche/http2/test_tools/random_decoder_test_base.h"
2
3 #include <stddef.h>
4
5 #include <functional>
6 #include <set>
7 #include <type_traits>
8
9 #include "quiche/http2/decoder/decode_buffer.h"
10 #include "quiche/http2/decoder/decode_status.h"
11 #include "quiche/http2/test_tools/http2_random.h"
12 #include "quiche/common/platform/api/quiche_logging.h"
13 #include "quiche/common/platform/api/quiche_test.h"
14 #include "quiche/common/quiche_callbacks.h"
15
16 namespace http2 {
17 namespace test {
18 namespace {
19 const char kData[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
20 const bool kReturnNonZeroOnFirst = true;
21 const bool kMayReturnZeroOnFirst = false;
22
23 // Confirm the behavior of various parts of RandomDecoderTest.
24 class RandomDecoderTestTest : public RandomDecoderTest {
25 public:
RandomDecoderTestTest()26 RandomDecoderTestTest() : data_db_(kData) {
27 QUICHE_CHECK_EQ(sizeof kData, 8u);
28 }
29
30 protected:
31 typedef quiche::MultiUseCallback<DecodeStatus(DecodeBuffer* db)> DecodingFn;
32
StartDecoding(DecodeBuffer * db)33 DecodeStatus StartDecoding(DecodeBuffer* db) override {
34 ++start_decoding_calls_;
35 if (start_decoding_fn_) {
36 return start_decoding_fn_(db);
37 }
38 return DecodeStatus::kDecodeError;
39 }
40
ResumeDecoding(DecodeBuffer * db)41 DecodeStatus ResumeDecoding(DecodeBuffer* db) override {
42 ++resume_decoding_calls_;
43 if (resume_decoding_fn_) {
44 return resume_decoding_fn_(db);
45 }
46 return DecodeStatus::kDecodeError;
47 }
48
StopDecodeOnDone()49 bool StopDecodeOnDone() override {
50 ++stop_decode_on_done_calls_;
51 if (override_stop_decode_on_done_) {
52 return sub_stop_decode_on_done_;
53 }
54 return RandomDecoderTest::StopDecodeOnDone();
55 }
56
57 size_t start_decoding_calls_ = 0;
58 size_t resume_decoding_calls_ = 0;
59 size_t stop_decode_on_done_calls_ = 0;
60
61 DecodingFn start_decoding_fn_;
62 DecodingFn resume_decoding_fn_;
63
64 DecodeBuffer data_db_;
65
66 bool sub_stop_decode_on_done_ = true;
67 bool override_stop_decode_on_done_ = true;
68 };
69
70 // Decode a single byte on the StartDecoding call, then stop.
TEST_F(RandomDecoderTestTest,StopOnStartPartiallyDone)71 TEST_F(RandomDecoderTestTest, StopOnStartPartiallyDone) {
72 start_decoding_fn_ = [this](DecodeBuffer* db) {
73 EXPECT_EQ(1u, start_decoding_calls_);
74 // Make sure the correct buffer is being used.
75 EXPECT_EQ(kData, db->cursor());
76 EXPECT_EQ(sizeof kData, db->Remaining());
77 db->DecodeUInt8();
78 return DecodeStatus::kDecodeDone;
79 };
80
81 EXPECT_EQ(DecodeStatus::kDecodeDone,
82 DecodeSegments(&data_db_, SelectRemaining()));
83 EXPECT_EQ(1u, data_db_.Offset());
84 // StartDecoding should only be called once from each call to DecodeSegments.
85 EXPECT_EQ(1u, start_decoding_calls_);
86 EXPECT_EQ(0u, resume_decoding_calls_);
87 EXPECT_EQ(1u, stop_decode_on_done_calls_);
88 }
89
90 // Stop decoding upon return from the first ResumeDecoding call.
TEST_F(RandomDecoderTestTest,StopOnResumePartiallyDone)91 TEST_F(RandomDecoderTestTest, StopOnResumePartiallyDone) {
92 start_decoding_fn_ = [this](DecodeBuffer* db) {
93 EXPECT_EQ(1u, start_decoding_calls_);
94 db->DecodeUInt8();
95 return DecodeStatus::kDecodeInProgress;
96 };
97 resume_decoding_fn_ = [this](DecodeBuffer* db) {
98 EXPECT_EQ(1u, resume_decoding_calls_);
99 // Make sure the correct buffer is being used.
100 EXPECT_EQ(data_db_.cursor(), db->cursor());
101 db->DecodeUInt16();
102 return DecodeStatus::kDecodeDone;
103 };
104
105 // Check that the base class honors it's member variable stop_decode_on_done_.
106 override_stop_decode_on_done_ = false;
107 stop_decode_on_done_ = true;
108
109 EXPECT_EQ(DecodeStatus::kDecodeDone,
110 DecodeSegments(&data_db_, SelectRemaining()));
111 EXPECT_EQ(3u, data_db_.Offset());
112 EXPECT_EQ(1u, start_decoding_calls_);
113 EXPECT_EQ(1u, resume_decoding_calls_);
114 EXPECT_EQ(1u, stop_decode_on_done_calls_);
115 }
116
117 // Decode a random sized chunks, always reporting back kDecodeInProgress.
TEST_F(RandomDecoderTestTest,InProgressWhenEmpty)118 TEST_F(RandomDecoderTestTest, InProgressWhenEmpty) {
119 start_decoding_fn_ = [this](DecodeBuffer* db) {
120 EXPECT_EQ(1u, start_decoding_calls_);
121 // Consume up to 2 bytes.
122 if (db->HasData()) {
123 db->DecodeUInt8();
124 if (db->HasData()) {
125 db->DecodeUInt8();
126 }
127 }
128 return DecodeStatus::kDecodeInProgress;
129 };
130 resume_decoding_fn_ = [](DecodeBuffer* db) {
131 // Consume all available bytes.
132 if (db->HasData()) {
133 db->AdvanceCursor(db->Remaining());
134 }
135 return DecodeStatus::kDecodeInProgress;
136 };
137
138 EXPECT_EQ(DecodeStatus::kDecodeInProgress,
139 DecodeSegments(&data_db_, SelectRandom(kMayReturnZeroOnFirst)));
140 EXPECT_TRUE(data_db_.Empty());
141 EXPECT_EQ(1u, start_decoding_calls_);
142 EXPECT_LE(1u, resume_decoding_calls_);
143 EXPECT_EQ(0u, stop_decode_on_done_calls_);
144 }
145
TEST_F(RandomDecoderTestTest,DoneExactlyAtEnd)146 TEST_F(RandomDecoderTestTest, DoneExactlyAtEnd) {
147 start_decoding_fn_ = [this](DecodeBuffer* db) {
148 EXPECT_EQ(1u, start_decoding_calls_);
149 EXPECT_EQ(1u, db->Remaining());
150 EXPECT_EQ(1u, db->FullSize());
151 db->DecodeUInt8();
152 return DecodeStatus::kDecodeInProgress;
153 };
154 resume_decoding_fn_ = [this](DecodeBuffer* db) {
155 EXPECT_EQ(1u, db->Remaining());
156 EXPECT_EQ(1u, db->FullSize());
157 db->DecodeUInt8();
158 if (data_db_.Remaining() == 1) {
159 return DecodeStatus::kDecodeDone;
160 }
161 return DecodeStatus::kDecodeInProgress;
162 };
163 override_stop_decode_on_done_ = true;
164 sub_stop_decode_on_done_ = true;
165
166 EXPECT_EQ(DecodeStatus::kDecodeDone, DecodeSegments(&data_db_, SelectOne()));
167 EXPECT_EQ(0u, data_db_.Remaining());
168 EXPECT_EQ(1u, start_decoding_calls_);
169 EXPECT_EQ((sizeof kData) - 1, resume_decoding_calls_);
170 // Didn't need to call StopDecodeOnDone because we didn't finish early.
171 EXPECT_EQ(0u, stop_decode_on_done_calls_);
172 }
173
TEST_F(RandomDecoderTestTest,DecodeSeveralWaysToEnd)174 TEST_F(RandomDecoderTestTest, DecodeSeveralWaysToEnd) {
175 // Each call to StartDecoding or ResumeDecoding will consume all that is
176 // available. When all the data has been consumed, returns kDecodeDone.
177 size_t decoded_since_start = 0;
178 auto shared_fn = [&decoded_since_start, this](DecodeBuffer* db) {
179 decoded_since_start += db->Remaining();
180 db->AdvanceCursor(db->Remaining());
181 EXPECT_EQ(0u, db->Remaining());
182 if (decoded_since_start == data_db_.FullSize()) {
183 return DecodeStatus::kDecodeDone;
184 }
185 return DecodeStatus::kDecodeInProgress;
186 };
187
188 start_decoding_fn_ = [&decoded_since_start, shared_fn](DecodeBuffer* db) {
189 decoded_since_start = 0;
190 return shared_fn(db);
191 };
192 resume_decoding_fn_ = shared_fn;
193
194 Validator validator = ValidateDoneAndEmpty();
195
196 EXPECT_TRUE(DecodeAndValidateSeveralWays(&data_db_, kMayReturnZeroOnFirst,
197 validator));
198
199 // We should have reached the end.
200 EXPECT_EQ(0u, data_db_.Remaining());
201
202 // We currently have 4 ways of decoding; update this if that changes.
203 EXPECT_EQ(4u, start_decoding_calls_);
204
205 // Didn't need to call StopDecodeOnDone because we didn't finish early.
206 EXPECT_EQ(0u, stop_decode_on_done_calls_);
207 }
208
TEST_F(RandomDecoderTestTest,DecodeTwoWaysAndStopEarly)209 TEST_F(RandomDecoderTestTest, DecodeTwoWaysAndStopEarly) {
210 // On the second decode, return kDecodeDone before finishing.
211 size_t decoded_since_start = 0;
212 auto shared_fn = [&decoded_since_start, this](DecodeBuffer* db) {
213 uint32_t amount = db->Remaining();
214 if (start_decoding_calls_ == 2 && amount > 1) {
215 amount = 1;
216 }
217 decoded_since_start += amount;
218 db->AdvanceCursor(amount);
219 if (decoded_since_start == data_db_.FullSize()) {
220 return DecodeStatus::kDecodeDone;
221 }
222 if (decoded_since_start > 1 && start_decoding_calls_ == 2) {
223 return DecodeStatus::kDecodeDone;
224 }
225 return DecodeStatus::kDecodeInProgress;
226 };
227
228 start_decoding_fn_ = [&decoded_since_start, shared_fn](DecodeBuffer* db) {
229 decoded_since_start = 0;
230 return shared_fn(db);
231 };
232 resume_decoding_fn_ = shared_fn;
233
234 // We expect the first and second to succeed, but the second to end at a
235 // different offset, which DecodeAndValidateSeveralWays should complain about.
236 Validator validator = [this](const DecodeBuffer& /*input*/,
237 DecodeStatus status) -> AssertionResult {
238 if (start_decoding_calls_ <= 2 && status != DecodeStatus::kDecodeDone) {
239 return ::testing::AssertionFailure()
240 << "Expected DecodeStatus::kDecodeDone, not " << status;
241 }
242 if (start_decoding_calls_ > 2) {
243 return ::testing::AssertionFailure()
244 << "How did we get to pass " << start_decoding_calls_;
245 }
246 return ::testing::AssertionSuccess();
247 };
248
249 EXPECT_FALSE(DecodeAndValidateSeveralWays(&data_db_, kMayReturnZeroOnFirst,
250 validator));
251 EXPECT_EQ(2u, start_decoding_calls_);
252 EXPECT_EQ(1u, stop_decode_on_done_calls_);
253 }
254
TEST_F(RandomDecoderTestTest,DecodeThreeWaysAndError)255 TEST_F(RandomDecoderTestTest, DecodeThreeWaysAndError) {
256 // Return kDecodeError from ResumeDecoding on the third decoding pass.
257 size_t decoded_since_start = 0;
258 auto shared_fn = [&decoded_since_start, this](DecodeBuffer* db) {
259 if (start_decoding_calls_ == 3 && decoded_since_start > 0) {
260 return DecodeStatus::kDecodeError;
261 }
262 uint32_t amount = db->Remaining();
263 if (start_decoding_calls_ == 3 && amount > 1) {
264 amount = 1;
265 }
266 decoded_since_start += amount;
267 db->AdvanceCursor(amount);
268 if (decoded_since_start == data_db_.FullSize()) {
269 return DecodeStatus::kDecodeDone;
270 }
271 return DecodeStatus::kDecodeInProgress;
272 };
273
274 start_decoding_fn_ = [&decoded_since_start, shared_fn](DecodeBuffer* db) {
275 decoded_since_start = 0;
276 return shared_fn(db);
277 };
278 resume_decoding_fn_ = shared_fn;
279
280 Validator validator = ValidateDoneAndEmpty();
281 EXPECT_FALSE(DecodeAndValidateSeveralWays(&data_db_, kReturnNonZeroOnFirst,
282 validator));
283 EXPECT_EQ(3u, start_decoding_calls_);
284 EXPECT_EQ(0u, stop_decode_on_done_calls_);
285 }
286
287 // CorruptEnum should produce lots of different values. On the assumption that
288 // the enum gets at least a byte of storage, we should be able to produce
289 // 256 distinct values.
TEST(CorruptEnumTest,ManyValues)290 TEST(CorruptEnumTest, ManyValues) {
291 std::set<uint64_t> values;
292 DecodeStatus status;
293 QUICHE_LOG(INFO) << "sizeof status = " << sizeof status;
294 Http2Random rng;
295 for (int ndx = 0; ndx < 256; ++ndx) {
296 CorruptEnum(&status, &rng);
297 values.insert(static_cast<uint64_t>(status));
298 }
299 }
300
301 // In practice the underlying type is an int, and currently that is 4 bytes.
302 typedef typename std::underlying_type<DecodeStatus>::type DecodeStatusUT;
303
304 struct CorruptEnumTestStruct {
305 DecodeStatusUT filler1;
306 DecodeStatus status;
307 DecodeStatusUT filler2;
308 };
309
310 // CorruptEnum should only overwrite the enum, not any adjacent storage.
TEST(CorruptEnumTest,CorruptsOnlyEnum)311 TEST(CorruptEnumTest, CorruptsOnlyEnum) {
312 Http2Random rng;
313 for (const DecodeStatusUT filler : {DecodeStatusUT(), ~DecodeStatusUT()}) {
314 QUICHE_LOG(INFO) << "filler=0x" << std::hex << filler;
315 CorruptEnumTestStruct s;
316 s.filler1 = filler;
317 s.filler2 = filler;
318 for (int ndx = 0; ndx < 256; ++ndx) {
319 CorruptEnum(&s.status, &rng);
320 EXPECT_EQ(s.filler1, filler);
321 EXPECT_EQ(s.filler2, filler);
322 }
323 }
324 }
325
326 } // namespace
327 } // namespace test
328 } // namespace http2
329