1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
20
21 #include <memory>
22 #include <string>
23
24 #include "absl/cleanup/cleanup.h"
25 #include "absl/random/random.h"
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/types/optional.h"
30 #include "gmock/gmock.h"
31 #include "gtest/gtest.h"
32
33 #include <grpc/event_engine/memory_allocator.h>
34 #include <grpc/grpc.h>
35 #include <grpc/slice.h>
36 #include <grpc/status.h>
37 #include <grpc/support/alloc.h>
38
39 #include "src/core/lib/gprpp/ref_counted_ptr.h"
40 #include "src/core/lib/gprpp/status_helper.h"
41 #include "src/core/lib/gprpp/time.h"
42 #include "src/core/lib/iomgr/exec_ctx.h"
43 #include "src/core/lib/resource_quota/arena.h"
44 #include "src/core/lib/resource_quota/memory_quota.h"
45 #include "src/core/lib/resource_quota/resource_quota.h"
46 #include "src/core/lib/slice/slice.h"
47 #include "src/core/lib/transport/error_utils.h"
48 #include "test/core/util/parse_hexstring.h"
49 #include "test/core/util/slice_splitter.h"
50 #include "test/core/util/test_config.h"
51
52 namespace grpc_core {
53 namespace {
54
55 const uint32_t kFailureIsConnectionError = 1;
56 const uint32_t kWithPriority = 2;
57 const uint32_t kEndOfStream = 4;
58 const uint32_t kEndOfHeaders = 8;
59
60 struct TestInput {
61 std::string input;
62 absl::StatusOr<std::string> expected_parse;
63 uint32_t flags;
64 };
65
66 struct Test {
67 std::string name;
68 absl::optional<size_t> table_size;
69 absl::optional<size_t> max_metadata_size;
70 std::vector<TestInput> inputs;
71 };
72
73 // Produce a nice name to print next to each test case for gtest.
NameFromConfig(const::testing::TestParamInfo<Test> & config)74 inline const char* NameFromConfig(
75 const ::testing::TestParamInfo<Test>& config) {
76 return config.param.name.c_str();
77 }
78
79 class ParseTest : public ::testing::TestWithParam<Test> {
80 public:
ParseTest()81 ParseTest() { grpc_init(); }
82
~ParseTest()83 ~ParseTest() override {
84 {
85 ExecCtx exec_ctx;
86 parser_.reset();
87 }
88
89 grpc_shutdown();
90 }
91
SetUp()92 void SetUp() override {
93 parser_ = std::make_unique<HPackParser>();
94 if (GetParam().table_size.has_value()) {
95 parser_->hpack_table()->SetMaxBytes(GetParam().table_size.value());
96 EXPECT_TRUE(parser_->hpack_table()->SetCurrentTableSize(
97 GetParam().table_size.value()));
98 }
99 }
100
IsStreamError(const absl::Status & status)101 static bool IsStreamError(const absl::Status& status) {
102 intptr_t stream_id;
103 return grpc_error_get_int(status, StatusIntProperty::kStreamId, &stream_id);
104 }
105
TestVector(grpc_slice_split_mode mode,absl::optional<size_t> max_metadata_size,std::string hexstring,absl::StatusOr<std::string> expect,uint32_t flags)106 void TestVector(grpc_slice_split_mode mode,
107 absl::optional<size_t> max_metadata_size,
108 std::string hexstring, absl::StatusOr<std::string> expect,
109 uint32_t flags) {
110 MemoryAllocator memory_allocator = MemoryAllocator(
111 ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator(
112 "test"));
113 auto arena = MakeScopedArena(1024, &memory_allocator);
114 ExecCtx exec_ctx;
115 auto input = ParseHexstring(hexstring);
116 grpc_slice* slices;
117 size_t nslices;
118 size_t i;
119 absl::BitGen bitgen;
120
121 grpc_metadata_batch b;
122
123 parser_->BeginFrame(
124 &b, max_metadata_size.value_or(4096), max_metadata_size.value_or(4096),
125 (flags & kEndOfStream)
126 ? HPackParser::Boundary::EndOfStream
127 : ((flags & kEndOfHeaders) ? HPackParser::Boundary::EndOfHeaders
128 : HPackParser::Boundary::None),
129 flags & kWithPriority ? HPackParser::Priority::Included
130 : HPackParser::Priority::None,
131 HPackParser::LogInfo{1, HPackParser::LogInfo::kHeaders, false});
132
133 grpc_split_slices(mode, const_cast<grpc_slice*>(&input.c_slice()), 1,
134 &slices, &nslices);
135 auto cleanup_slices = absl::MakeCleanup([slices, nslices] {
136 for (size_t i = 0; i < nslices; i++) {
137 grpc_slice_unref(slices[i]);
138 }
139 gpr_free(slices);
140 });
141
142 bool saw_error = false;
143 for (i = 0; i < nslices; i++) {
144 ExecCtx exec_ctx;
145 auto err =
146 parser_->Parse(slices[i], i == nslices - 1, absl::BitGenRef(bitgen),
147 /*call_tracer=*/nullptr);
148 if (!err.ok() && (flags & kFailureIsConnectionError) == 0) {
149 EXPECT_TRUE(IsStreamError(err)) << err;
150 }
151 if (!saw_error && !err.ok()) {
152 // one byte at a time mode might fail with a stream error early
153 if (mode == GRPC_SLICE_SPLIT_ONE_BYTE &&
154 (flags & kFailureIsConnectionError) && IsStreamError(err)) {
155 continue;
156 }
157 grpc_status_code code;
158 std::string message;
159 grpc_error_get_status(err, Timestamp::InfFuture(), &code, &message,
160 nullptr, nullptr);
161 EXPECT_EQ(code, static_cast<grpc_status_code>(expect.status().code()))
162 << err << " slice[" << i << "]; input: " << hexstring
163 << "\nTABLE:\n"
164 << parser_->hpack_table()->TestOnlyDynamicTableAsString();
165 EXPECT_THAT(message, ::testing::HasSubstr(expect.status().message()))
166 << err << " slice[" << i << "]; input: " << hexstring
167 << "\nTABLE:\n"
168 << parser_->hpack_table()->TestOnlyDynamicTableAsString();
169 saw_error = true;
170 if (flags & kFailureIsConnectionError) return;
171 }
172 }
173
174 if (!saw_error) {
175 EXPECT_TRUE(expect.ok()) << expect.status();
176 }
177
178 if (expect.ok()) {
179 TestEncoder encoder;
180 b.Encode(&encoder);
181 EXPECT_EQ(encoder.result(), *expect) << "Input: " << hexstring;
182 }
183 }
184
185 private:
186 class TestEncoder {
187 public:
result()188 std::string result() { return out_; }
189
Encode(const Slice & key,const Slice & value)190 void Encode(const Slice& key, const Slice& value) {
191 out_.append(absl::StrCat(key.as_string_view(), ": ",
192 value.as_string_view(), "\n"));
193 }
194
195 template <typename T, typename V>
Encode(T,const V & v)196 void Encode(T, const V& v) {
197 out_.append(
198 absl::StrCat(T::key(), ": ", T::Encode(v).as_string_view(), "\n"));
199 }
200
201 private:
202 std::string out_;
203 };
204
205 std::unique_ptr<HPackParser> parser_;
206 };
207
TEST_P(ParseTest,WholeSlices)208 TEST_P(ParseTest, WholeSlices) {
209 for (const auto& input : GetParam().inputs) {
210 TestVector(GRPC_SLICE_SPLIT_MERGE_ALL, GetParam().max_metadata_size,
211 input.input, input.expected_parse, input.flags);
212 }
213 }
214
TEST_P(ParseTest,OneByteAtATime)215 TEST_P(ParseTest, OneByteAtATime) {
216 for (const auto& input : GetParam().inputs) {
217 TestVector(GRPC_SLICE_SPLIT_ONE_BYTE, GetParam().max_metadata_size,
218 input.input, input.expected_parse, input.flags);
219 }
220 }
221
222 INSTANTIATE_TEST_SUITE_P(
223 ParseTest, ParseTest,
224 ::testing::Values(
225 Test{"RfcTestD2",
226 {},
227 {},
228 {
229 // D.2.1
230 {"400a 6375 7374 6f6d 2d6b 6579 0d63 7573"
231 "746f 6d2d 6865 6164 6572",
232 "custom-key: custom-header\n", 0},
233 // D.2.2
234 {"040c 2f73 616d 706c 652f 7061 7468", ":path: /sample/path\n",
235 0},
236 // D.2.3
237 {"1008 7061 7373 776f 7264 0673 6563 7265"
238 "74",
239 "password: secret\n", 0},
240 // D.2.4
241 {"82", ":method: GET\n", 0},
242 }},
243 Test{"RfcTestD3",
244 {},
245 {},
246 {
247 // D.3.1
248 {"8286 8441 0f77 7777 2e65 7861 6d70 6c65"
249 "2e63 6f6d",
250 ":path: /\n"
251 ":authority: www.example.com\n"
252 ":method: GET\n"
253 ":scheme: http\n",
254 0},
255 // D.3.2
256 {"8286 84be 5808 6e6f 2d63 6163 6865",
257 ":path: /\n"
258 ":authority: www.example.com\n"
259 ":method: GET\n"
260 ":scheme: http\n"
261 "cache-control: no-cache\n",
262 0},
263 // D.3.3
264 {"8287 85bf 400a 6375 7374 6f6d 2d6b 6579"
265 "0c63 7573 746f 6d2d 7661 6c75 65",
266 ":path: /index.html\n"
267 ":authority: www.example.com\n"
268 ":method: GET\n"
269 ":scheme: https\n"
270 "custom-key: custom-value\n",
271 0},
272 }},
273 Test{"RfcTestD4",
274 {},
275 {},
276 {
277 // D.4.1
278 {"8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4"
279 "ff",
280 ":path: /\n"
281 ":authority: www.example.com\n"
282 ":method: GET\n"
283 ":scheme: http\n",
284 0},
285 // D.4.2
286 {"8286 84be 5886 a8eb 1064 9cbf",
287 ":path: /\n"
288 ":authority: www.example.com\n"
289 ":method: GET\n"
290 ":scheme: http\n"
291 "cache-control: no-cache\n",
292 0},
293 // D.4.3
294 {"8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925"
295 "a849 e95b b8e8 b4bf",
296 ":path: /index.html\n"
297 ":authority: www.example.com\n"
298 ":method: GET\n"
299 ":scheme: https\n"
300 "custom-key: custom-value\n",
301 0},
302 }},
303 Test{"RfcTestD5",
304 {256},
305 {},
306 {
307 // D.5.1
308 {"4803 3330 3258 0770 7269 7661 7465 611d"
309 "4d6f 6e2c 2032 3120 4f63 7420 3230 3133"
310 "2032 303a 3133 3a32 3120 474d 546e 1768"
311 "7474 7073 3a2f 2f77 7777 2e65 7861 6d70"
312 "6c65 2e63 6f6d",
313 ":status: 302\n"
314 "cache-control: private\n"
315 "date: Mon, 21 Oct 2013 20:13:21 GMT\n"
316 "location: https://www.example.com\n",
317 0},
318 // D.5.2
319 {"4803 3330 37c1 c0bf",
320 ":status: 307\n"
321 "cache-control: private\n"
322 "date: Mon, 21 Oct 2013 20:13:21 GMT\n"
323 "location: https://www.example.com\n",
324 0},
325 // D.5.3
326 {"88c1 611d 4d6f 6e2c 2032 3120 4f63 7420"
327 "3230 3133 2032 303a 3133 3a32 3220 474d"
328 "54c0 5a04 677a 6970 7738 666f 6f3d 4153"
329 "444a 4b48 514b 425a 584f 5157 454f 5049"
330 "5541 5851 5745 4f49 553b 206d 6178 2d61"
331 "6765 3d33 3630 303b 2076 6572 7369 6f6e"
332 "3d31",
333 ":status: 200\n"
334 "cache-control: private\n"
335 "date: Mon, 21 Oct 2013 20:13:22 GMT\n"
336 "location: https://www.example.com\n"
337 "content-encoding: gzip\n"
338 "set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; "
339 "version=1\n",
340 0},
341 }},
342 Test{"RfcTestD6",
343 {256},
344 {},
345 {
346 // D.6.1
347 {"4882 6402 5885 aec3 771a 4b61 96d0 7abe"
348 "9410 54d4 44a8 2005 9504 0b81 66e0 82a6"
349 "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8"
350 "e9ae 82ae 43d3",
351 ":status: 302\n"
352 "cache-control: private\n"
353 "date: Mon, 21 Oct 2013 20:13:21 GMT\n"
354 "location: https://www.example.com\n",
355 0},
356 // D.6.2
357 {"4883 640e ffc1 c0bf",
358 ":status: 307\n"
359 "cache-control: private\n"
360 "date: Mon, 21 Oct 2013 20:13:21 GMT\n"
361 "location: https://www.example.com\n",
362 0},
363 // D.6.3
364 {"88c1 6196 d07a be94 1054 d444 a820 0595"
365 "040b 8166 e084 a62d 1bff c05a 839b d9ab"
366 "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b"
367 "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f"
368 "9587 3160 65c0 03ed 4ee5 b106 3d50 07",
369 ":status: 200\n"
370 "cache-control: private\n"
371 "date: Mon, 21 Oct 2013 20:13:22 GMT\n"
372 "location: https://www.example.com\n"
373 "content-encoding: gzip\n"
374 "set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; "
375 "version=1\n",
376 0},
377 }},
378 Test{"IllegalHpackTableGrowth",
379 {},
380 {1024},
381 {{"3fc43fc4", absl::InternalError("Attempt to make hpack table"),
382 kFailureIsConnectionError}}},
383 Test{"FuzzerFoundInvalidHpackIndexFuzzerFound1",
384 {},
385 {},
386 {{"3ba4a41007f0a40f2d62696e8b632a5b29a40fa4a4281007f0",
387 absl::InternalError("Invalid HPACK index received"),
388 kFailureIsConnectionError}}},
389 Test{"FuzzerFoundMultipleTableSizeChanges1",
390 {},
391 {},
392 {{"2aa41007f0a40f2d62696e8163a41f1f00275bf0692862a4dbf0f00963",
393 absl::InternalError(
394 "More than two max table size changes in a single frame"),
395 kFailureIsConnectionError}}},
396 Test{"FuzzerFoundIllegalHeaderKey1",
397 {},
398 {},
399 {{"2aa41007f0a40f2d62696e8363271f00275bf06928626e2d213fa40fdbf0212"
400 "8215cf00963",
401 absl::InternalError("Illegal header key"), 0}}},
402 Test{"FuzzerFoundMultipleTableSizeChanges2",
403 {},
404 {},
405 {{"a4a41007f0a40f2d62696e8b635b29282d2762696e3b0921213fa41fdbf0211"
406 "007f07b282d62696ef009215c0921e51fe91b3b3f47ed5b282821215cf0",
407 absl::InternalError(
408 "More than two max table size changes in a single frame"),
409 kFailureIsConnectionError}}},
410 Test{
411 "FuzzerFoundInterOverflow1",
412 {},
413 {},
414 {{"696969696969696969696969696969696969696969696969696969696969696"
415 "969696969696969696969696969696969696969696969696969696969696969"
416 "6969696969696969696969696969bababababababababababababababababab"
417 "abababababababababababababababababababababababababababababababa"
418 "bababababababababababababababababababababababababababababababab"
419 "abababababaa4a41007f0a40f2d62696e8bffffffffffffffffffffffffffff"
420 "ffffffffffff632a5b29a428a42d0fdbf027f0628363696e092121",
421 absl::InternalError("integer overflow in hpack integer decoding"),
422 kEndOfHeaders | kFailureIsConnectionError}}},
423 Test{"StatusIsAnInteger",
424 {},
425 {},
426 {{"0e 00 00 df",
427 absl::InternalError("Error parsing ':status' metadata"), 0}}},
428 Test{"BinaryMetadataFromBase64",
429 {},
430 {},
431 {
432 // Binary metadata: created using:
433 // tools/codegen/core/gen_header_frame.py
434 // --compression inc --no_framing --output hexstr
435 // < test/core/transport/chttp2/binary-metadata.headers
436 {"40 09 61 2e 62 2e 63 2d 62 69 6e 0c 62 32 31 6e 4d 6a 41 79 "
437 "4d 51 3d 3d",
438 "a.b.c-bin: omg2021\n", 0},
439 }},
440 Test{"Base64LegalEncoding",
441 {},
442 {},
443 {// Binary metadata: created using:
444 // tools/codegen/core/gen_header_frame.py
445 // --compression inc --no_framing --output hexstr
446 // < test/core/transport/chttp2/bad-base64.headers
447 {"4009612e622e632d62696e1c6c75636b696c7920666f722075732c206974"
448 "27732074756573646179",
449 absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
450 "illegal base64 encoding"),
451 0},
452 {"be",
453 absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
454 "illegal base64 encoding"),
455 0}}},
456 Test{"TeIsTrailers",
457 {},
458 {},
459 {// created using:
460 // tools/codegen/core/gen_header_frame.py
461 // --compression inc --no_framing --output hexstr
462 // < test/core/transport/chttp2/bad-te.headers
463 {"400274650767617262616765",
464 absl::InternalError("Error parsing 'te' metadata"), 0},
465 {"be", absl::InternalError("Error parsing 'te' metadata"), 0}}},
466 Test{"MetadataSizeLimitCheck",
467 {},
468 128,
469 {
470 {// Generated with: tools/codegen/core/gen_header_frame.py
471 // --compression inc --output hexstr --no_framing <
472 // test/core/transport/chttp2/large-metadata.headers
473 "40096164616c64726964610a6272616e64796275636b40086164616c6772"
474 "696d04746f6f6b4008616d6172616e74680a6272616e64796275636b4008"
475 "616e67656c6963610762616767696e73",
476 absl::ResourceExhaustedError(
477 "received metadata size exceeds hard limit"),
478 kEndOfHeaders},
479 // Should be able to look up the added elements individually
480 // (do not corrupt the hpack table test!)
481 {"be", "angelica: baggins\n", kEndOfHeaders},
482 {"bf", "amaranth: brandybuck\n", kEndOfHeaders},
483 {"c0", "adalgrim: took\n", kEndOfHeaders},
484 {"c1", "adaldrida: brandybuck\n", kEndOfHeaders},
485 // But not as a whole - that exceeds metadata limits for one
486 // request again
487 {"bebfc0c1",
488 absl::ResourceExhaustedError(
489 "received metadata size exceeds hard limit"),
490 0},
491 }},
492 Test{
493 "SingleByteBE",
494 {},
495 {},
496 {{"be", absl::InternalError("Invalid HPACK index received"),
497 kFailureIsConnectionError}},
498 },
499 Test{
500 "SingleByte80",
501 {},
502 {},
503 {{"80", absl::InternalError("Illegal hpack op code"),
504 kFailureIsConnectionError}},
505 },
506 Test{
507 "SingleByte29",
508 {},
509 {},
510 {{"29", "", kFailureIsConnectionError}},
511 },
512 Test{
513 "EmptyWithPriority",
514 {},
515 {},
516 {{"", "", kWithPriority}},
517 },
518 Test{
519 "SingleByteF5",
520 {},
521 {},
522 {{"f5", absl::InternalError("Invalid HPACK index received"),
523 kFailureIsConnectionError}},
524 },
525 Test{
526 "SingleByte0f",
527 {},
528 {},
529 {{"0f", "", 0}},
530 },
531 Test{
532 "SingleByte7f",
533 {},
534 {},
535 {{"7f", "", 0}},
536 },
537 Test{
538 "FuzzerCoverage1bffffff7c1b",
539 {},
540 {},
541 {{"1bffffff7c1b",
542 absl::ResourceExhaustedError(
543 "received metadata size exceeds hard limit"),
544 0}},
545 },
546 Test{
547 "FuzzerCoverageffffffffff00ff",
548 {},
549 {},
550 {{"ffffffffff00ff",
551 absl::InternalError("Invalid HPACK index received"),
552 kFailureIsConnectionError}},
553 },
554 Test{
555 "FuzzerCoverageIntegerOverflow2",
556 {},
557 {},
558 {{"ff8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8"
559 "d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d"
560 "8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8"
561 "d8d8d8d8d8d8d8d",
562 absl::InternalError("integer overflow in hpack integer decoding"),
563 kFailureIsConnectionError}}},
564 Test{
565 "FuzzerCoverageMetadataLimits",
566 {},
567 {9},
568 {{"3f6672616d6573207ba2020656e645f6f665f686561646572733a2074727565a"
569 "2020656e645f6f665f73747265616d3a2074727565a202073746f705f6275666"
570 "66572696e675f61667465725f7365676d656e74733a2039a202070617273653a"
571 "20225c3030305c3030305c3030305c3030305c3030305c3030305c3030305c30"
572 "30305c3030305c3030305c3030305c3030305c3030305c3030305c3030305c30"
573 "30305c3030305c3030305c3030305c3030305c3030305c3030305c3030305c",
574 absl::ResourceExhaustedError(
575 "received metadata size exceeds hard limit"),
576 kWithPriority}}},
577 Test{"FuzzerCoverage52046772706300073a737461747573033230300e7f",
578 {},
579 {},
580 {{"52046772706300073a737461747573033230300e7f",
581 ":status: 200\naccept-ranges: grpc\n", 0}}},
582 Test{"FuzzerCoveragea4a41007f0a40f2d62696e8beda42d5b63272129a410626907",
583 {},
584 {},
585 {{"a4a41007f0a40f2d62696e8beda42d5b63272129a410626907",
586 absl::InternalError("Illegal header key"), 0}}},
587 Test{
588 "HpackTableSizeWithBase64",
589 // haiku segment: 149bytes*2, a:a segment: 34 bytes
590 // So we arrange for one less than the total so we force a hpack
591 // table overflow
592 {149 * 2 + 34 - 1},
593 {},
594 {
595 {// Generated with: tools/codegen/core/gen_header_frame.py
596 // --compression inc --output hexstr --no_framing <
597 // test/core/transport/chttp2/long-base64.headers
598 "4005782d62696e70516d467a5a545930494756755932396b6157356e4f67"
599 "704a644342305957746c6379426961573568636e6b675a47463059534268"
600 "626d5167625746725a584d6761585167644756346443344b56584e6c5a6e5"
601 "67349475a766369427a644739796157356e49475a706247567a4c673d3d",
602 // Haiku by Bard.
603 "x-bin: Base64 encoding:\nIt takes binary data and makes it "
604 "text.\nUseful for storing files.\n",
605 0},
606 // Should go into the hpack table (x-bin: ... is 149 bytes long
607 // by hpack rules)
608 {"be",
609 "x-bin: Base64 encoding:\nIt takes binary data and "
610 "makes it text.\nUseful for storing files.\n",
611 0},
612 // Add another copy
613 {"4005782d62696e70516d467a5a545930494756755932396b6157356e4f67"
614 "704a644342305957746c6379426961573568636e6b675a47463059534268"
615 "626d5167625746725a584d6761585167644756346443344b56584e6c5a6e5"
616 "67349475a766369427a644739796157356e49475a706247567a4c673d3d",
617 "x-bin: Base64 encoding:\nIt takes binary data and makes it "
618 "text.\nUseful for storing files.\n",
619 0},
620 // 149*2 == 298, so we should have two copies in the hpack table
621 {"bebf",
622 "x-bin: Base64 encoding:\nIt takes binary data and makes it "
623 "text.\nUseful for storing files.\n"
624 "x-bin: Base64 encoding:\nIt takes binary data and makes it "
625 "text.\nUseful for storing files.\n",
626 0},
627 // Add some very short headers (should push the first long thing
628 // out)
629 // Generated with: tools/codegen/core/gen_header_frame.py
630 // --compression inc --output hexstr --no_framing <
631 // test/core/transport/chttp2/short.headers
632 {"4001610161", "a: a\n", 0},
633 // First two entries should be what was just pushed and then one
634 // long entry
635 {"bebf",
636 "a: a\nx-bin: Base64 encoding:\nIt takes binary data and "
637 "makes "
638 "it text.\nUseful for storing files.\n",
639 0},
640 // Third entry should be unprobable (it's no longer in the
641 // table!)
642 {"c0", absl::InternalError("Invalid HPACK index received"),
643 kFailureIsConnectionError},
644 }},
645 Test{
646 "HpackTableSizeWithBase64AndHuffman",
647 // haiku segment: 149bytes*2, a:a segment: 34 bytes
648 // So we arrange for one less than the total so we force a hpack
649 // table overflow
650 {149 * 2 + 34 - 1},
651 {},
652 {
653 {// Generated with: tools/codegen/core/gen_header_frame.py
654 // --compression inc --output hexstr --no_framing --huff <
655 // test/core/transport/chttp2/long-base64.headers
656 "4005782d62696edbd94e1f7fbbf983262e36f313fd47c9bab54d5e592f5d0"
657 "73e49a09eae987c9b9c95759bf7161073dd7678e9d9347cb0d9fbf9a261fe"
658 "6c9a4c5c5a92f359b8fe69a3f6ae28c98bf7b90d77dc989ff43e4dd59317e"
659 "d71e2e3ef3cd041",
660 // Haiku by Bard.
661 "x-bin: Base64 encoding:\nIt takes binary data and makes it "
662 "text.\nUseful for storing files.\n",
663 0},
664 // Should go into the hpack table (x-bin: ... is 149 bytes long
665 // by hpack rules)
666 {"be",
667 "x-bin: Base64 encoding:\nIt takes binary data and "
668 "makes it text.\nUseful for storing files.\n",
669 0},
670 // Add another copy
671 {"4005782d62696edbd94e1f7fbbf983262e36f313fd47c9bab54d5e592f5d0"
672 "73e49a09eae987c9b9c95759bf7161073dd7678e9d9347cb0d9fbf9a261fe"
673 "6c9a4c5c5a92f359b8fe69a3f6ae28c98bf7b90d77dc989ff43e4dd59317e"
674 "d71e2e3ef3cd041",
675 "x-bin: Base64 encoding:\nIt takes binary data and makes it "
676 "text.\nUseful for storing files.\n",
677 0},
678 // 149*2 == 298, so we should have two copies in the hpack table
679 {"bebf",
680 "x-bin: Base64 encoding:\nIt takes binary data and makes it "
681 "text.\nUseful for storing files.\n"
682 "x-bin: Base64 encoding:\nIt takes binary data and makes it "
683 "text.\nUseful for storing files.\n",
684 0},
685 // Add some very short headers (should push the first long thing
686 // out)
687 // Generated with: tools/codegen/core/gen_header_frame.py
688 // --compression inc --output hexstr --no_framing <
689 // test/core/transport/chttp2/short.headers
690 {"4001610161", "a: a\n", 0},
691 // First two entries should be what was just pushed and then one
692 // long entry
693 {"bebf",
694 "a: a\nx-bin: Base64 encoding:\nIt takes binary data and "
695 "makes "
696 "it text.\nUseful for storing files.\n",
697 0},
698 // Third entry should be unprobable (it's no longer in the
699 // table!)
700 {"c0", absl::InternalError("Invalid HPACK index received"),
701 kFailureIsConnectionError},
702 }},
703 Test{"SingleByte7a", {}, {}, {{"7a", "", 0}}},
704 Test{"SingleByte60",
705 {},
706 {},
707 {{"60",
708 absl::InternalError("Incomplete header at the end of a "
709 "header/continuation sequence"),
710 kEndOfStream | kFailureIsConnectionError}}},
711 Test{"FuzzerFoundMultipleTableSizeChanges3",
712 {},
713 {},
714 {{"89", ":status: 204\n", 0},
715 {"89", ":status: 204\n", 0},
716 {"393939393939393939393939393939393939393939",
717 absl::InternalError(
718 "More than two max table size changes in a single frame"),
719 kFailureIsConnectionError}}},
720 Test{"FuzzerCoverage4005782d62696edbd94e1f7etc",
721 {},
722 {},
723 {{"4005782d62696edbd94e1f7fbbf983267e36a313fd47c9bab54d5e592f5d",
724 "", 0}}},
725 Test{"FuzzerCoverage72656672657368",
726 {},
727 {},
728 {{"72656672657368", "", 0}}},
729 Test{"FuzzerCoverage66e6645f74Then66645f74",
730 {},
731 {},
732 {{"66e6645f74", "", 0}, {"66645f74", "", 0}}},
733 Test{
734 "MixedCaseHeadersAreStreamErrors",
735 {},
736 {},
737 {{// Generated with: tools/codegen/core/gen_header_frame.py
738 // --compression inc --output hexstr --no_framing <
739 // test/core/transport/chttp2/MiXeD-CaSe.headers
740 "400a4d695865442d436153651073686f756c64206e6f74207061727365",
741 absl::InternalError("Illegal header key: MiXeD-CaSe"), 0},
742 {// Looking up with hpack indices should work, but also return
743 // error
744 "be", absl::InternalError("Illegal header key: MiXeD-CaSe"), 0}}},
745 Test{
746 "FuzzerCoverageIntegerOverflow3",
747 {},
748 {},
749 {{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
750 absl::InternalError("integer overflow in hpack integer decoding"),
751 kFailureIsConnectionError}}},
752 Test{"Dadadadadada",
753 {},
754 {},
755 {{"dadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadad"
756 "adadadadadadadadadadadadadadadadadadadadadadadadadadadadadadada"
757 "dadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadad"
758 "adadadadadadadadadadadadadadadadadadada",
759 absl::InternalError("Invalid HPACK index received"),
760 kWithPriority | kFailureIsConnectionError}}},
761 Test{"MaliciousVarintEncoding",
762 {},
763 {},
764 {{"1f80808080808080808080808080808080808080808080808080808080",
765 absl::InternalError(
766 "Malicious varint encoding detected in HPACK stream"),
767 kFailureIsConnectionError}}}),
768 NameFromConfig);
769
770 } // namespace
771 } // namespace grpc_core
772
main(int argc,char ** argv)773 int main(int argc, char** argv) {
774 grpc::testing::TestEnvironment env(&argc, argv);
775 ::testing::InitGoogleTest(&argc, argv);
776 return RUN_ALL_TESTS();
777 }
778