grpc 1.53.0 → 1.53.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grpc might be problematic. Click here for more details.

Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +4 -2
  3. data/include/grpc/impl/grpc_types.h +11 -2
  4. data/src/core/ext/filters/client_channel/http_proxy.cc +1 -1
  5. data/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc +1 -1
  6. data/src/core/ext/transport/chttp2/transport/bin_encoder.cc +12 -8
  7. data/src/core/ext/transport/chttp2/transport/bin_encoder.h +5 -1
  8. data/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +33 -2
  9. data/src/core/ext/transport/chttp2/transport/hpack_encoder.cc +118 -222
  10. data/src/core/ext/transport/chttp2/transport/hpack_encoder.h +295 -113
  11. data/src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc +2 -0
  12. data/src/core/ext/transport/chttp2/transport/hpack_encoder_table.h +2 -0
  13. data/src/core/ext/transport/chttp2/transport/hpack_parser.cc +466 -273
  14. data/src/core/ext/transport/chttp2/transport/hpack_parser.h +7 -3
  15. data/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc +14 -12
  16. data/src/core/ext/transport/chttp2/transport/hpack_parser_table.h +9 -1
  17. data/src/core/ext/transport/chttp2/transport/internal.h +2 -0
  18. data/src/core/ext/transport/chttp2/transport/parsing.cc +6 -0
  19. data/src/core/lib/backoff/random_early_detection.cc +31 -0
  20. data/src/core/lib/backoff/random_early_detection.h +59 -0
  21. data/src/core/lib/iomgr/endpoint_pair.h +2 -2
  22. data/src/core/lib/iomgr/endpoint_pair_posix.cc +2 -2
  23. data/src/core/lib/iomgr/endpoint_pair_windows.cc +1 -1
  24. data/src/core/lib/surface/validate_metadata.cc +43 -42
  25. data/src/core/lib/surface/validate_metadata.h +9 -0
  26. data/src/core/lib/transport/metadata_batch.cc +4 -4
  27. data/src/core/lib/transport/metadata_batch.h +153 -15
  28. data/src/core/lib/transport/parsed_metadata.h +19 -9
  29. data/src/ruby/lib/grpc/version.rb +1 -1
  30. metadata +5 -3
@@ -38,7 +38,6 @@
38
38
  #include "absl/types/span.h"
39
39
  #include "absl/types/variant.h"
40
40
 
41
- #include <grpc/status.h>
42
41
  #include <grpc/support/log.h>
43
42
 
44
43
  #include "src/core/ext/transport/chttp2/transport/decode_huff.h"
@@ -46,9 +45,11 @@
46
45
  #include "src/core/lib/debug/stats.h"
47
46
  #include "src/core/lib/debug/stats_data.h"
48
47
  #include "src/core/lib/debug/trace.h"
48
+ #include "src/core/lib/gprpp/crash.h"
49
49
  #include "src/core/lib/gprpp/status_helper.h"
50
50
  #include "src/core/lib/slice/slice.h"
51
51
  #include "src/core/lib/slice/slice_refcount.h"
52
+ #include "src/core/lib/surface/validate_metadata.h"
52
53
  #include "src/core/lib/transport/parsed_metadata.h"
53
54
 
54
55
  // IWYU pragma: no_include <type_traits>
@@ -80,6 +81,40 @@ struct Base64InverseTable {
80
81
  };
81
82
 
82
83
  constexpr Base64InverseTable kBase64InverseTable;
84
+
85
+ absl::Status EnsureStreamError(absl::Status error) {
86
+ if (error.ok()) return error;
87
+ return grpc_error_set_int(std::move(error), StatusIntProperty::kStreamId, 0);
88
+ }
89
+
90
+ bool IsStreamError(const absl::Status& status) {
91
+ intptr_t stream_id;
92
+ return grpc_error_get_int(status, StatusIntProperty::kStreamId, &stream_id);
93
+ }
94
+
95
+ class MetadataSizeLimitExceededEncoder {
96
+ public:
97
+ explicit MetadataSizeLimitExceededEncoder(std::string& summary)
98
+ : summary_(summary) {}
99
+
100
+ void Encode(const Slice& key, const Slice& value) {
101
+ AddToSummary(key.as_string_view(), value.size());
102
+ }
103
+
104
+ template <typename Key, typename Value>
105
+ void Encode(Key, const Value& value) {
106
+ AddToSummary(Key::key(), EncodedSizeOfKey(Key(), value));
107
+ }
108
+
109
+ private:
110
+ void AddToSummary(absl::string_view key,
111
+ size_t value_length) GPR_ATTRIBUTE_NOINLINE {
112
+ absl::StrAppend(&summary_, " ", key, ":",
113
+ hpack_constants::SizeForEntry(key.size(), value_length),
114
+ "B");
115
+ }
116
+ std::string& summary_;
117
+ };
83
118
  } // namespace
84
119
 
85
120
  // Input tracks the current byte through the input data and provides it
@@ -121,7 +156,8 @@ class HPackParser::Input {
121
156
  // of stream
122
157
  absl::optional<uint8_t> Next() {
123
158
  if (end_of_stream()) {
124
- return UnexpectedEOF(absl::optional<uint8_t>());
159
+ UnexpectedEOF();
160
+ return absl::optional<uint8_t>();
125
161
  }
126
162
  return *begin_++;
127
163
  }
@@ -187,7 +223,10 @@ class HPackParser::Input {
187
223
  // Parse a string prefix
188
224
  absl::optional<StringPrefix> ParseStringPrefix() {
189
225
  auto cur = Next();
190
- if (!cur.has_value()) return {};
226
+ if (!cur.has_value()) {
227
+ GPR_DEBUG_ASSERT(eof_error());
228
+ return {};
229
+ }
191
230
  // Huffman if the top bit is 1
192
231
  const bool huff = (*cur & 0x80) != 0;
193
232
  // String length
@@ -195,14 +234,19 @@ class HPackParser::Input {
195
234
  if (strlen == 0x7f) {
196
235
  // all ones ==> varint string length
197
236
  auto v = ParseVarint(0x7f);
198
- if (!v.has_value()) return {};
237
+ if (!v.has_value()) {
238
+ GPR_DEBUG_ASSERT(eof_error());
239
+ return {};
240
+ }
199
241
  strlen = *v;
200
242
  }
201
243
  return StringPrefix{strlen, huff};
202
244
  }
203
245
 
204
246
  // Check if we saw an EOF.. must be verified before looking at TakeError
205
- bool eof_error() const { return eof_error_; }
247
+ bool eof_error() const {
248
+ return eof_error_ || (!error_.ok() && !IsStreamError(error_));
249
+ }
206
250
 
207
251
  // Extract the parse error, leaving the current error as NONE.
208
252
  grpc_error_handle TakeError() {
@@ -211,34 +255,33 @@ class HPackParser::Input {
211
255
  return out;
212
256
  }
213
257
 
214
- // Set the current error - allows the rest of the code not to need to pass
215
- // around StatusOr<> which would be prohibitive here.
216
- GPR_ATTRIBUTE_NOINLINE void SetError(grpc_error_handle error) {
217
- if (!error_.ok() || eof_error_) {
218
- return;
219
- }
220
- error_ = error;
221
- begin_ = end_;
258
+ bool has_error() const { return !error_.ok(); }
259
+
260
+ // Set the current error - tweaks the error to include a stream id so that
261
+ // chttp2 does not close the connection.
262
+ // Intended for errors that are specific to a stream and recoverable.
263
+ // Callers should ensure that any hpack table updates happen.
264
+ GPR_ATTRIBUTE_NOINLINE void SetErrorAndContinueParsing(
265
+ grpc_error_handle error) {
266
+ GPR_ASSERT(!error.ok());
267
+ // StreamId is used as a signal to skip this stream but keep the connection
268
+ // alive
269
+ SetError(EnsureStreamError(std::move(error)));
222
270
  }
223
271
 
224
- // If no error is set, set it to the value produced by error_factory.
225
- // Return return_value unchanged.
226
- template <typename F, typename T>
227
- GPR_ATTRIBUTE_NOINLINE T MaybeSetErrorAndReturn(F error_factory,
228
- T return_value) {
229
- if (!error_.ok() || eof_error_) return return_value;
230
- error_ = error_factory();
272
+ // Set the current error, and skip past remaining bytes.
273
+ // Intended for unrecoverable errors, with the expectation that they will
274
+ // close the connection on return to chttp2.
275
+ GPR_ATTRIBUTE_NOINLINE void SetErrorAndStopParsing(grpc_error_handle error) {
276
+ GPR_ASSERT(!error.ok());
277
+ SetError(std::move(error));
231
278
  begin_ = end_;
232
- return return_value;
233
279
  }
234
280
 
235
- // Set the error to an unexpected eof, and return result (code golfed as this
236
- // is a common case)
237
- template <typename T>
238
- T UnexpectedEOF(T return_value) {
239
- if (!error_.ok()) return return_value;
281
+ // Set the error to an unexpected eof
282
+ void UnexpectedEOF() {
283
+ if (!error_.ok() && !IsStreamError(error_)) return;
240
284
  eof_error_ = true;
241
- return return_value;
242
285
  }
243
286
 
244
287
  // Update the frontier - signifies we've successfully parsed another element
@@ -251,14 +294,24 @@ class HPackParser::Input {
251
294
  // Helper to set the error to out of range for ParseVarint
252
295
  absl::optional<uint32_t> ParseVarintOutOfRange(uint32_t value,
253
296
  uint8_t last_byte) {
254
- return MaybeSetErrorAndReturn(
255
- [value, last_byte] {
256
- return GRPC_ERROR_CREATE(absl::StrFormat(
257
- "integer overflow in hpack integer decoding: have 0x%08x, "
258
- "got byte 0x%02x on byte 5",
259
- value, last_byte));
260
- },
261
- absl::optional<uint32_t>());
297
+ SetErrorAndStopParsing(absl::InternalError(absl::StrFormat(
298
+ "integer overflow in hpack integer decoding: have 0x%08x, "
299
+ "got byte 0x%02x on byte 5",
300
+ value, last_byte)));
301
+ return absl::optional<uint32_t>();
302
+ }
303
+
304
+ // If no error is set, set it to the given error (i.e. first error wins)
305
+ // Do not use this directly, instead use SetErrorAndContinueParsing or
306
+ // SetErrorAndStopParsing.
307
+ void SetError(grpc_error_handle error) {
308
+ if (!error_.ok() || eof_error_) {
309
+ if (!IsStreamError(error) && IsStreamError(error_)) {
310
+ error_ = std::move(error); // connection errors dominate
311
+ }
312
+ return;
313
+ }
314
+ error_ = std::move(error);
262
315
  }
263
316
 
264
317
  // Refcount if we are backed by a slice
@@ -279,6 +332,21 @@ class HPackParser::Input {
279
332
  // management characteristics
280
333
  class HPackParser::String {
281
334
  public:
335
+ // ParseResult carries both a ParseStatus and the parsed string
336
+ struct ParseResult;
337
+ // Result of parsing a string
338
+ enum class ParseStatus {
339
+ // Parsed OK
340
+ kOk,
341
+ // Parse reached end of the current frame
342
+ kEof,
343
+ // Parse failed due to a huffman decode error
344
+ kParseHuffFailed,
345
+ // Parse failed due to a base64 decode error
346
+ kUnbase64Failed,
347
+ };
348
+
349
+ String() : value_(absl::Span<const uint8_t>()) {}
282
350
  String(const String&) = delete;
283
351
  String& operator=(const String&) = delete;
284
352
  String(String&& other) noexcept : value_(std::move(other.value_)) {
@@ -308,72 +376,10 @@ class HPackParser::String {
308
376
  }
309
377
 
310
378
  // Parse a non-binary string
311
- static absl::optional<String> Parse(Input* input) {
312
- auto pfx = input->ParseStringPrefix();
313
- if (!pfx.has_value()) return {};
314
- if (pfx->huff) {
315
- // Huffman coded
316
- std::vector<uint8_t> output;
317
- auto v = ParseHuff(input, pfx->length,
318
- [&output](uint8_t c) { output.push_back(c); });
319
- if (!v) return {};
320
- return String(std::move(output));
321
- }
322
- return ParseUncompressed(input, pfx->length);
323
- }
379
+ static ParseResult Parse(Input* input);
324
380
 
325
381
  // Parse a binary string
326
- static absl::optional<String> ParseBinary(Input* input) {
327
- auto pfx = input->ParseStringPrefix();
328
- if (!pfx.has_value()) return {};
329
- if (!pfx->huff) {
330
- if (pfx->length > 0 && input->peek() == 0) {
331
- // 'true-binary'
332
- input->Advance(1);
333
- return ParseUncompressed(input, pfx->length - 1);
334
- }
335
- // Base64 encoded... pull out the string, then unbase64 it
336
- auto base64 = ParseUncompressed(input, pfx->length);
337
- if (!base64.has_value()) return {};
338
- return Unbase64(input, std::move(*base64));
339
- } else {
340
- // Huffman encoded...
341
- std::vector<uint8_t> decompressed;
342
- // State here says either we don't know if it's base64 or binary, or we do
343
- // and what is it.
344
- enum class State { kUnsure, kBinary, kBase64 };
345
- State state = State::kUnsure;
346
- auto decompressed_ok =
347
- ParseHuff(input, pfx->length, [&state, &decompressed](uint8_t c) {
348
- if (state == State::kUnsure) {
349
- // First byte... if it's zero it's binary
350
- if (c == 0) {
351
- // Save the type, and skip the zero
352
- state = State::kBinary;
353
- return;
354
- } else {
355
- // Flag base64, store this value
356
- state = State::kBase64;
357
- }
358
- }
359
- // Non-first byte, or base64 first byte
360
- decompressed.push_back(c);
361
- });
362
- if (!decompressed_ok) return {};
363
- switch (state) {
364
- case State::kUnsure:
365
- // No bytes, empty span
366
- return String(absl::Span<const uint8_t>());
367
- case State::kBinary:
368
- // Binary, we're done
369
- return String(std::move(decompressed));
370
- case State::kBase64:
371
- // Base64 - unpack it
372
- return Unbase64(input, String(std::move(decompressed)));
373
- }
374
- GPR_UNREACHABLE_CODE(abort(););
375
- }
376
- }
382
+ static ParseResult ParseBinary(Input* input);
377
383
 
378
384
  private:
379
385
  void AppendBytes(const uint8_t* data, size_t length);
@@ -385,54 +391,27 @@ class HPackParser::String {
385
391
  // Parse some huffman encoded bytes, using output(uint8_t b) to emit each
386
392
  // decoded byte.
387
393
  template <typename Out>
388
- static bool ParseHuff(Input* input, uint32_t length, Out output) {
394
+ static ParseStatus ParseHuff(Input* input, uint32_t length, Out output) {
389
395
  // If there's insufficient bytes remaining, return now.
390
396
  if (input->remaining() < length) {
391
- return input->UnexpectedEOF(false);
397
+ input->UnexpectedEOF();
398
+ GPR_DEBUG_ASSERT(input->eof_error());
399
+ return ParseStatus::kEof;
392
400
  }
393
401
  // Grab the byte range, and iterate through it.
394
402
  const uint8_t* p = input->cur_ptr();
395
403
  input->Advance(length);
396
- return HuffDecoder<Out>(output, p, p + length).Run();
404
+ return HuffDecoder<Out>(output, p, p + length).Run()
405
+ ? ParseStatus::kOk
406
+ : ParseStatus::kParseHuffFailed;
397
407
  }
398
408
 
399
409
  // Parse some uncompressed string bytes.
400
- static absl::optional<String> ParseUncompressed(Input* input,
401
- uint32_t length) {
402
- // Check there's enough bytes
403
- if (input->remaining() < length) {
404
- return input->UnexpectedEOF(absl::optional<String>());
405
- }
406
- auto* refcount = input->slice_refcount();
407
- auto* p = input->cur_ptr();
408
- input->Advance(length);
409
- if (refcount != nullptr) {
410
- return String(refcount, p, p + length);
411
- } else {
412
- return String(absl::Span<const uint8_t>(p, length));
413
- }
414
- }
410
+ static ParseResult ParseUncompressed(Input* input, uint32_t length,
411
+ uint32_t wire_size);
415
412
 
416
413
  // Turn base64 encoded bytes into not base64 encoded bytes.
417
- // Only takes input to set an error on failure.
418
- static absl::optional<String> Unbase64(Input* input, String s) {
419
- absl::optional<std::vector<uint8_t>> result;
420
- if (auto* p = absl::get_if<Slice>(&s.value_)) {
421
- result = Unbase64Loop(p->begin(), p->end());
422
- }
423
- if (auto* p = absl::get_if<absl::Span<const uint8_t>>(&s.value_)) {
424
- result = Unbase64Loop(p->begin(), p->end());
425
- }
426
- if (auto* p = absl::get_if<std::vector<uint8_t>>(&s.value_)) {
427
- result = Unbase64Loop(p->data(), p->data() + p->size());
428
- }
429
- if (!result.has_value()) {
430
- return input->MaybeSetErrorAndReturn(
431
- [] { return GRPC_ERROR_CREATE("illegal base64 encoding"); },
432
- absl::optional<String>());
433
- }
434
- return String(std::move(*result));
435
- }
414
+ static ParseResult Unbase64(String s);
436
415
 
437
416
  // Main loop for Unbase64
438
417
  static absl::optional<std::vector<uint8_t>> Unbase64Loop(const uint8_t* cur,
@@ -519,24 +498,154 @@ class HPackParser::String {
519
498
  absl::variant<Slice, absl::Span<const uint8_t>, std::vector<uint8_t>> value_;
520
499
  };
521
500
 
501
+ struct HPackParser::String::ParseResult {
502
+ ParseResult() = delete;
503
+ ParseResult(ParseStatus status, size_t wire_size, String value)
504
+ : status(status), wire_size(wire_size), value(std::move(value)) {}
505
+ ParseStatus status;
506
+ size_t wire_size;
507
+ String value;
508
+ };
509
+
510
+ HPackParser::String::ParseResult HPackParser::String::ParseUncompressed(
511
+ Input* input, uint32_t length, uint32_t wire_size) {
512
+ // Check there's enough bytes
513
+ if (input->remaining() < length) {
514
+ input->UnexpectedEOF();
515
+ GPR_DEBUG_ASSERT(input->eof_error());
516
+ return ParseResult{ParseStatus::kEof, wire_size, String{}};
517
+ }
518
+ auto* refcount = input->slice_refcount();
519
+ auto* p = input->cur_ptr();
520
+ input->Advance(length);
521
+ if (refcount != nullptr) {
522
+ return ParseResult{ParseStatus::kOk, wire_size,
523
+ String(refcount, p, p + length)};
524
+ } else {
525
+ return ParseResult{ParseStatus::kOk, wire_size,
526
+ String(absl::Span<const uint8_t>(p, length))};
527
+ }
528
+ }
529
+
530
+ HPackParser::String::ParseResult HPackParser::String::Unbase64(String s) {
531
+ absl::optional<std::vector<uint8_t>> result;
532
+ if (auto* p = absl::get_if<Slice>(&s.value_)) {
533
+ result = Unbase64Loop(p->begin(), p->end());
534
+ }
535
+ if (auto* p = absl::get_if<absl::Span<const uint8_t>>(&s.value_)) {
536
+ result = Unbase64Loop(p->begin(), p->end());
537
+ }
538
+ if (auto* p = absl::get_if<std::vector<uint8_t>>(&s.value_)) {
539
+ result = Unbase64Loop(p->data(), p->data() + p->size());
540
+ }
541
+ if (!result.has_value()) {
542
+ return ParseResult{ParseStatus::kUnbase64Failed, s.string_view().length(),
543
+ String{}};
544
+ }
545
+ return ParseResult{ParseStatus::kOk, s.string_view().length(),
546
+ String(std::move(*result))};
547
+ }
548
+
549
+ HPackParser::String::ParseResult HPackParser::String::Parse(Input* input) {
550
+ auto pfx = input->ParseStringPrefix();
551
+ if (!pfx.has_value()) {
552
+ GPR_DEBUG_ASSERT(input->eof_error());
553
+ return ParseResult{ParseStatus::kEof, 0, String{}};
554
+ }
555
+ if (pfx->huff) {
556
+ // Huffman coded
557
+ std::vector<uint8_t> output;
558
+ ParseStatus sts = ParseHuff(input, pfx->length,
559
+ [&output](uint8_t c) { output.push_back(c); });
560
+ size_t wire_len = output.size();
561
+ return ParseResult{sts, wire_len, String(std::move(output))};
562
+ }
563
+ return ParseUncompressed(input, pfx->length, pfx->length);
564
+ }
565
+
566
+ HPackParser::String::ParseResult HPackParser::String::ParseBinary(
567
+ Input* input) {
568
+ auto pfx = input->ParseStringPrefix();
569
+ if (!pfx.has_value()) {
570
+ GPR_DEBUG_ASSERT(input->eof_error());
571
+ return ParseResult{ParseStatus::kEof, 0, String{}};
572
+ }
573
+ if (!pfx->huff) {
574
+ if (pfx->length > 0 && input->peek() == 0) {
575
+ // 'true-binary'
576
+ input->Advance(1);
577
+ return ParseUncompressed(input, pfx->length - 1, pfx->length);
578
+ }
579
+ // Base64 encoded... pull out the string, then unbase64 it
580
+ auto base64 = ParseUncompressed(input, pfx->length, pfx->length);
581
+ if (base64.status != ParseStatus::kOk) return base64;
582
+ return Unbase64(std::move(base64.value));
583
+ } else {
584
+ // Huffman encoded...
585
+ std::vector<uint8_t> decompressed;
586
+ // State here says either we don't know if it's base64 or binary, or we do
587
+ // and what is it.
588
+ enum class State { kUnsure, kBinary, kBase64 };
589
+ State state = State::kUnsure;
590
+ auto sts =
591
+ ParseHuff(input, pfx->length, [&state, &decompressed](uint8_t c) {
592
+ if (state == State::kUnsure) {
593
+ // First byte... if it's zero it's binary
594
+ if (c == 0) {
595
+ // Save the type, and skip the zero
596
+ state = State::kBinary;
597
+ return;
598
+ } else {
599
+ // Flag base64, store this value
600
+ state = State::kBase64;
601
+ }
602
+ }
603
+ // Non-first byte, or base64 first byte
604
+ decompressed.push_back(c);
605
+ });
606
+ if (sts != ParseStatus::kOk) {
607
+ return ParseResult{sts, 0, String{}};
608
+ }
609
+ switch (state) {
610
+ case State::kUnsure:
611
+ // No bytes, empty span
612
+ return ParseResult{ParseStatus::kOk, 0,
613
+ String(absl::Span<const uint8_t>())};
614
+ case State::kBinary:
615
+ // Binary, we're done
616
+ {
617
+ size_t wire_len = decompressed.size();
618
+ return ParseResult{ParseStatus::kOk, wire_len,
619
+ String(std::move(decompressed))};
620
+ }
621
+ case State::kBase64:
622
+ // Base64 - unpack it
623
+ return Unbase64(String(std::move(decompressed)));
624
+ }
625
+ GPR_UNREACHABLE_CODE(abort(););
626
+ }
627
+ }
628
+
522
629
  // Parser parses one key/value pair from a byte stream.
523
630
  class HPackParser::Parser {
524
631
  public:
525
- Parser(Input* input, grpc_metadata_batch* metadata_buffer,
526
- uint32_t metadata_size_limit, HPackTable* table,
632
+ Parser(Input* input, grpc_metadata_batch* metadata_buffer, HPackTable* table,
527
633
  uint8_t* dynamic_table_updates_allowed, uint32_t* frame_length,
528
- LogInfo log_info)
634
+ RandomEarlyDetection* metadata_early_detection, LogInfo log_info)
529
635
  : input_(input),
530
636
  metadata_buffer_(metadata_buffer),
531
637
  table_(table),
532
638
  dynamic_table_updates_allowed_(dynamic_table_updates_allowed),
533
639
  frame_length_(frame_length),
534
- metadata_size_limit_(metadata_size_limit),
640
+ metadata_early_detection_(metadata_early_detection),
535
641
  log_info_(log_info) {}
536
642
 
537
643
  // Skip any priority bits, or return false on failure
538
644
  bool SkipPriority() {
539
- if (input_->remaining() < 5) return input_->UnexpectedEOF(false);
645
+ if (input_->remaining() < 5) {
646
+ input_->UnexpectedEOF();
647
+ return false;
648
+ }
540
649
  input_->Advance(5);
541
650
  return true;
542
651
  }
@@ -609,8 +718,9 @@ class HPackParser::Parser {
609
718
  case 8:
610
719
  if (cur == 0x80) {
611
720
  // illegal value.
612
- return input_->MaybeSetErrorAndReturn(
613
- [] { return GRPC_ERROR_CREATE("Illegal hpack op code"); }, false);
721
+ input_->SetErrorAndStopParsing(
722
+ absl::InternalError("Illegal hpack op code"));
723
+ return false;
614
724
  }
615
725
  ABSL_FALLTHROUGH_INTENDED;
616
726
  case 9:
@@ -647,20 +757,31 @@ class HPackParser::Parser {
647
757
  type = "???";
648
758
  break;
649
759
  }
650
- gpr_log(GPR_DEBUG, "HTTP:%d:%s:%s: %s", log_info_.stream_id, type,
651
- log_info_.is_client ? "CLI" : "SVR", memento.DebugString().c_str());
760
+ gpr_log(GPR_DEBUG, "HTTP:%d:%s:%s: %s%s", log_info_.stream_id, type,
761
+ log_info_.is_client ? "CLI" : "SVR",
762
+ memento.md.DebugString().c_str(),
763
+ memento.parse_status.ok()
764
+ ? ""
765
+ : absl::StrCat(
766
+ " (parse error: ", memento.parse_status.ToString(), ")")
767
+ .c_str());
652
768
  }
653
769
 
654
- bool EmitHeader(const HPackTable::Memento& md) {
770
+ void EmitHeader(const HPackTable::Memento& md) {
655
771
  // Pass up to the transport
656
- if (GPR_UNLIKELY(metadata_buffer_ == nullptr)) return true;
657
- *frame_length_ += md.transport_size();
658
- if (GPR_UNLIKELY(*frame_length_ > metadata_size_limit_)) {
659
- return HandleMetadataSizeLimitExceeded(md);
772
+ *frame_length_ += md.md.transport_size();
773
+ if (!input_->has_error() &&
774
+ metadata_early_detection_->MustReject(*frame_length_)) {
775
+ // Reject any requests above hard metadata limit.
776
+ HandleMetadataHardSizeLimitExceeded(md);
777
+ }
778
+ if (!md.parse_status.ok()) {
779
+ // Reject any requests with invalid metadata.
780
+ HandleMetadataParseError(md.parse_status);
781
+ }
782
+ if (GPR_LIKELY(metadata_buffer_ != nullptr)) {
783
+ metadata_buffer_->Set(md.md);
660
784
  }
661
-
662
- metadata_buffer_->Set(md);
663
- return true;
664
785
  }
665
786
 
666
787
  bool FinishHeaderAndAddToTable(absl::optional<HPackTable::Memento> md) {
@@ -671,73 +792,149 @@ class HPackParser::Parser {
671
792
  LogHeader(*md);
672
793
  }
673
794
  // Emit whilst we own the metadata.
674
- auto r = EmitHeader(*md);
795
+ EmitHeader(*md);
675
796
  // Add to the hpack table
676
797
  grpc_error_handle err = table_->Add(std::move(*md));
677
798
  if (GPR_UNLIKELY(!err.ok())) {
678
- input_->SetError(err);
799
+ input_->SetErrorAndStopParsing(std::move(err));
679
800
  return false;
680
801
  };
681
- return r;
802
+ return true;
682
803
  }
683
804
 
684
805
  bool FinishHeaderOmitFromTable(absl::optional<HPackTable::Memento> md) {
685
806
  // Allow higher code to just pass in failures ... simplifies things a bit.
686
807
  if (!md.has_value()) return false;
687
- return FinishHeaderOmitFromTable(*md);
808
+ FinishHeaderOmitFromTable(*md);
809
+ return true;
688
810
  }
689
811
 
690
- bool FinishHeaderOmitFromTable(const HPackTable::Memento& md) {
812
+ void FinishHeaderOmitFromTable(const HPackTable::Memento& md) {
691
813
  // Log if desired
692
814
  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_chttp2_hpack_parser)) {
693
815
  LogHeader(md);
694
816
  }
695
- return EmitHeader(md);
817
+ EmitHeader(md);
696
818
  }
697
819
 
820
+ // Helper type to build a memento from a key & value, and to consolidate some
821
+ // tricky error path code.
822
+ class MementoBuilder {
823
+ public:
824
+ explicit MementoBuilder(Input* input, absl::string_view key_string,
825
+ absl::Status status = absl::OkStatus())
826
+ : input_(input), key_string_(key_string), status_(std::move(status)) {}
827
+
828
+ auto ErrorHandler() {
829
+ return [this](absl::string_view error, const Slice&) {
830
+ auto message =
831
+ absl::StrCat("Error parsing '", key_string_,
832
+ "' metadata: error=", error, " key=", key_string_);
833
+ gpr_log(GPR_ERROR, "%s", message.c_str());
834
+ if (status_.ok()) {
835
+ status_ = absl::InternalError(message);
836
+ }
837
+ };
838
+ }
839
+
840
+ HPackTable::Memento Build(ParsedMetadata<grpc_metadata_batch> memento) {
841
+ return HPackTable::Memento{std::move(memento), std::move(status_)};
842
+ }
843
+
844
+ // Handle the result of parsing a value.
845
+ // Returns true if parsing should continue, false if it should stop.
846
+ // Stores an error on the input if necessary.
847
+ bool HandleParseResult(String::ParseStatus status) {
848
+ auto continuable = [this](absl::string_view error) {
849
+ auto this_error = absl::InternalError(absl::StrCat(
850
+ "Error parsing '", key_string_, "' metadata: error=", error));
851
+ if (status_.ok()) status_ = this_error;
852
+ input_->SetErrorAndContinueParsing(std::move(this_error));
853
+ };
854
+ switch (status) {
855
+ case String::ParseStatus::kOk:
856
+ return true;
857
+ case String::ParseStatus::kParseHuffFailed:
858
+ input_->SetErrorAndStopParsing(
859
+ absl::InternalError("Huffman decoding failed"));
860
+ return false;
861
+ case String::ParseStatus::kUnbase64Failed:
862
+ continuable("illegal base64 encoding");
863
+ return true;
864
+ case String::ParseStatus::kEof:
865
+ GPR_DEBUG_ASSERT(input_->eof_error());
866
+ return false;
867
+ }
868
+ GPR_UNREACHABLE_CODE(return false);
869
+ }
870
+
871
+ private:
872
+ Input* input_;
873
+ absl::string_view key_string_;
874
+ absl::Status status_;
875
+ };
876
+
698
877
  // Parse a string encoded key and a string encoded value
699
878
  absl::optional<HPackTable::Memento> ParseLiteralKey() {
700
879
  auto key = String::Parse(input_);
701
- if (!key.has_value()) return {};
702
- auto value = ParseValueString(absl::EndsWith(key->string_view(), "-bin"));
703
- if (GPR_UNLIKELY(!value.has_value())) {
704
- return {};
880
+ switch (key.status) {
881
+ case String::ParseStatus::kOk:
882
+ break;
883
+ case String::ParseStatus::kParseHuffFailed:
884
+ input_->SetErrorAndStopParsing(
885
+ absl::InternalError("Huffman decoding failed"));
886
+ return absl::nullopt;
887
+ case String::ParseStatus::kUnbase64Failed:
888
+ Crash("unreachable");
889
+ case String::ParseStatus::kEof:
890
+ GPR_DEBUG_ASSERT(input_->eof_error());
891
+ return absl::nullopt;
705
892
  }
706
- auto key_string = key->string_view();
707
- auto value_slice = value->Take();
708
- const auto transport_size = key_string.size() + value_slice.size() +
709
- hpack_constants::kEntryOverhead;
710
- return grpc_metadata_batch::Parse(
711
- key->string_view(), std::move(value_slice), transport_size,
712
- [key_string](absl::string_view error, const Slice& value) {
713
- ReportMetadataParseError(key_string, error, value.as_string_view());
714
- });
893
+ auto key_string = key.value.string_view();
894
+ auto value = ParseValueString(absl::EndsWith(key_string, "-bin"));
895
+ MementoBuilder builder(input_, key_string,
896
+ EnsureStreamError(ValidateKey(key_string)));
897
+ if (!builder.HandleParseResult(value.status)) return absl::nullopt;
898
+ auto value_slice = value.value.Take();
899
+ const auto transport_size =
900
+ key_string.size() + value.wire_size + hpack_constants::kEntryOverhead;
901
+ return builder.Build(
902
+ grpc_metadata_batch::Parse(key_string, std::move(value_slice),
903
+ transport_size, builder.ErrorHandler()));
904
+ }
905
+
906
+ absl::Status ValidateKey(absl::string_view key) {
907
+ if (key == HttpSchemeMetadata::key() || key == HttpMethodMetadata::key() ||
908
+ key == HttpAuthorityMetadata::key() || key == HttpPathMetadata::key() ||
909
+ key == HttpStatusMetadata::key()) {
910
+ return absl::OkStatus();
911
+ }
912
+ return ValidateHeaderKeyIsLegal(key);
715
913
  }
716
914
 
717
915
  // Parse an index encoded key and a string encoded value
718
916
  absl::optional<HPackTable::Memento> ParseIdxKey(uint32_t index) {
719
917
  const auto* elem = table_->Lookup(index);
720
918
  if (GPR_UNLIKELY(elem == nullptr)) {
721
- return InvalidHPackIndexError(index,
722
- absl::optional<HPackTable::Memento>());
723
- }
724
- auto value = ParseValueString(elem->is_binary_header());
725
- if (GPR_UNLIKELY(!value.has_value())) return {};
726
- return elem->WithNewValue(
727
- value->Take(), [=](absl::string_view error, const Slice& value) {
728
- ReportMetadataParseError(elem->key(), error, value.as_string_view());
729
- });
730
- }
919
+ InvalidHPackIndexError(index);
920
+ return absl::optional<HPackTable::Memento>();
921
+ }
922
+ MementoBuilder builder(input_, elem->md.key(), elem->parse_status);
923
+ auto value = ParseValueString(elem->md.is_binary_header());
924
+ if (!builder.HandleParseResult(value.status)) return absl::nullopt;
925
+ return builder.Build(elem->md.WithNewValue(
926
+ value.value.Take(), value.wire_size, builder.ErrorHandler()));
927
+ };
731
928
 
732
929
  // Parse a varint index encoded key and a string encoded value
733
930
  absl::optional<HPackTable::Memento> ParseVarIdxKey(uint32_t offset) {
734
931
  auto index = input_->ParseVarint(offset);
735
- if (GPR_UNLIKELY(!index.has_value())) return {};
932
+ if (GPR_UNLIKELY(!index.has_value())) return absl::nullopt;
736
933
  return ParseIdxKey(*index);
737
934
  }
738
935
 
739
936
  // Parse a string, figuring out if it's binary or not by the key name.
740
- absl::optional<String> ParseValueString(bool is_binary) {
937
+ String::ParseResult ParseValueString(bool is_binary) {
741
938
  if (is_binary) {
742
939
  return String::ParseBinary(input_);
743
940
  } else {
@@ -751,26 +948,25 @@ class HPackParser::Parser {
751
948
  if (!index.has_value()) return false;
752
949
  const auto* elem = table_->Lookup(*index);
753
950
  if (GPR_UNLIKELY(elem == nullptr)) {
754
- return InvalidHPackIndexError(*index, false);
951
+ InvalidHPackIndexError(*index);
952
+ return false;
755
953
  }
756
- return FinishHeaderOmitFromTable(*elem);
954
+ FinishHeaderOmitFromTable(*elem);
955
+ return true;
757
956
  }
758
957
 
759
958
  // finish parsing a max table size change
760
959
  bool FinishMaxTableSize(absl::optional<uint32_t> size) {
761
960
  if (!size.has_value()) return false;
762
961
  if (*dynamic_table_updates_allowed_ == 0) {
763
- return input_->MaybeSetErrorAndReturn(
764
- [] {
765
- return GRPC_ERROR_CREATE(
766
- "More than two max table size changes in a single frame");
767
- },
768
- false);
962
+ input_->SetErrorAndStopParsing(absl::InternalError(
963
+ "More than two max table size changes in a single frame"));
964
+ return false;
769
965
  }
770
966
  (*dynamic_table_updates_allowed_)--;
771
967
  grpc_error_handle err = table_->SetCurrentTableSize(*size);
772
968
  if (!err.ok()) {
773
- input_->SetError(err);
969
+ input_->SetErrorAndStopParsing(std::move(err));
774
970
  return false;
775
971
  }
776
972
  return true;
@@ -778,88 +974,52 @@ class HPackParser::Parser {
778
974
 
779
975
  // Set an invalid hpack index error if no error has been set. Returns result
780
976
  // unmodified.
781
- template <typename R>
782
- R InvalidHPackIndexError(uint32_t index, R result) {
783
- return input_->MaybeSetErrorAndReturn(
784
- [this, index] {
785
- return grpc_error_set_int(
786
- grpc_error_set_int(
787
- GRPC_ERROR_CREATE("Invalid HPACK index received"),
788
- StatusIntProperty::kIndex, static_cast<intptr_t>(index)),
789
- StatusIntProperty::kSize,
790
- static_cast<intptr_t>(this->table_->num_entries()));
791
- },
792
- std::move(result));
793
- }
794
-
795
- class MetadataSizeLimitExceededEncoder {
796
- public:
797
- explicit MetadataSizeLimitExceededEncoder(std::string& summary)
798
- : summary_(summary) {}
799
-
800
- void Encode(const Slice& key, const Slice& value) {
801
- AddToSummary(key.as_string_view(), value.size());
802
- }
803
-
804
- template <typename Key, typename Value>
805
- void Encode(Key, const Value& value) {
806
- AddToSummary(Key::key(), EncodedSizeOfKey(Key(), value));
807
- }
977
+ void InvalidHPackIndexError(uint32_t index) {
978
+ input_->SetErrorAndStopParsing(grpc_error_set_int(
979
+ grpc_error_set_int(absl::InternalError("Invalid HPACK index received"),
980
+ StatusIntProperty::kIndex,
981
+ static_cast<intptr_t>(index)),
982
+ StatusIntProperty::kSize,
983
+ static_cast<intptr_t>(this->table_->num_entries())));
984
+ }
808
985
 
809
- private:
810
- void AddToSummary(absl::string_view key,
811
- size_t value_length) GPR_ATTRIBUTE_NOINLINE {
812
- absl::StrAppend(&summary_, " ", key, ":",
813
- hpack_constants::SizeForEntry(key.size(), value_length),
814
- "B");
986
+ GPR_ATTRIBUTE_NOINLINE
987
+ void HandleMetadataParseError(const absl::Status& status) {
988
+ if (metadata_buffer_ != nullptr) {
989
+ metadata_buffer_->Clear();
990
+ metadata_buffer_ = nullptr;
815
991
  }
816
- std::string& summary_;
817
- };
992
+ // StreamId is used as a signal to skip this stream but keep the connection
993
+ // alive
994
+ input_->SetErrorAndContinueParsing(status);
995
+ }
818
996
 
819
997
  GPR_ATTRIBUTE_NOINLINE
820
- bool HandleMetadataSizeLimitExceeded(const HPackTable::Memento& md) {
998
+ void HandleMetadataHardSizeLimitExceeded(const HPackTable::Memento& md) {
821
999
  // Collect a summary of sizes so far for debugging
822
1000
  // Do not collect contents, for fear of exposing PII.
823
1001
  std::string summary;
1002
+ std::string error_message;
824
1003
  if (metadata_buffer_ != nullptr) {
825
1004
  MetadataSizeLimitExceededEncoder encoder(summary);
826
1005
  metadata_buffer_->Encode(&encoder);
827
1006
  }
828
- summary =
829
- absl::StrCat("; adding ", md.key(), " (length ", md.transport_size(),
830
- "B)", summary.empty() ? "" : " to ", summary);
831
- if (metadata_buffer_ != nullptr) metadata_buffer_->Clear();
832
- // StreamId is used as a signal to skip this stream but keep the connection
833
- // alive
834
- return input_->MaybeSetErrorAndReturn(
835
- [this, summary = std::move(summary)] {
836
- return grpc_error_set_int(
837
- grpc_error_set_int(
838
- GRPC_ERROR_CREATE(absl::StrCat(
839
- "received initial metadata size exceeds limit (",
840
- *frame_length_, " vs. ", metadata_size_limit_, ")",
841
- summary)),
842
- StatusIntProperty::kRpcStatus,
843
- GRPC_STATUS_RESOURCE_EXHAUSTED),
844
- StatusIntProperty::kStreamId, 0);
845
- },
846
- false);
847
- }
848
-
849
- static void ReportMetadataParseError(absl::string_view key,
850
- absl::string_view error,
851
- absl::string_view value) {
852
- gpr_log(
853
- GPR_ERROR, "Error parsing metadata: %s",
854
- absl::StrCat("error=", error, " key=", key, " value=", value).c_str());
1007
+ summary = absl::StrCat("; adding ", md.md.key(), " (length ",
1008
+ md.md.transport_size(), "B)",
1009
+ summary.empty() ? "" : " to ", summary);
1010
+ error_message = absl::StrCat(
1011
+ "received metadata size exceeds hard limit (", *frame_length_, " vs. ",
1012
+ metadata_early_detection_->hard_limit(), ")", summary);
1013
+ HandleMetadataParseError(absl::ResourceExhaustedError(error_message));
855
1014
  }
856
1015
 
857
1016
  Input* const input_;
858
- grpc_metadata_batch* const metadata_buffer_;
1017
+ grpc_metadata_batch* metadata_buffer_;
859
1018
  HPackTable* const table_;
860
1019
  uint8_t* const dynamic_table_updates_allowed_;
861
1020
  uint32_t* const frame_length_;
862
- const uint32_t metadata_size_limit_;
1021
+ // Random early detection of metadata size limits.
1022
+ RandomEarlyDetection* metadata_early_detection_;
863
1023
  const LogInfo log_info_;
864
1024
  };
865
1025
 
@@ -881,8 +1041,10 @@ HPackParser::HPackParser() = default;
881
1041
  HPackParser::~HPackParser() = default;
882
1042
 
883
1043
  void HPackParser::BeginFrame(grpc_metadata_batch* metadata_buffer,
884
- uint32_t metadata_size_limit, Boundary boundary,
885
- Priority priority, LogInfo log_info) {
1044
+ uint32_t metadata_size_soft_limit,
1045
+ uint32_t metadata_size_hard_limit,
1046
+ Boundary boundary, Priority priority,
1047
+ LogInfo log_info) {
886
1048
  metadata_buffer_ = metadata_buffer;
887
1049
  if (metadata_buffer != nullptr) {
888
1050
  metadata_buffer->Set(GrpcStatusFromWire(), true);
@@ -891,7 +1053,9 @@ void HPackParser::BeginFrame(grpc_metadata_batch* metadata_buffer,
891
1053
  priority_ = priority;
892
1054
  dynamic_table_updates_allowed_ = 2;
893
1055
  frame_length_ = 0;
894
- metadata_size_limit_ = metadata_size_limit;
1056
+ metadata_early_detection_ = RandomEarlyDetection(
1057
+ /*soft_limit=*/metadata_size_soft_limit,
1058
+ /*hard_limit=*/metadata_size_hard_limit);
895
1059
  log_info_ = log_info;
896
1060
  }
897
1061
 
@@ -909,43 +1073,72 @@ grpc_error_handle HPackParser::Parse(const grpc_slice& slice, bool is_last) {
909
1073
  }
910
1074
 
911
1075
  grpc_error_handle HPackParser::ParseInput(Input input, bool is_last) {
912
- bool parsed_ok = ParseInputInner(&input);
913
- if (is_last) global_stats().IncrementHttp2MetadataSize(frame_length_);
914
- if (parsed_ok) return absl::OkStatus();
1076
+ ParseInputInner(&input);
1077
+ if (is_last) {
1078
+ if (metadata_early_detection_.Reject(frame_length_)) {
1079
+ HandleMetadataSoftSizeLimitExceeded(&input);
1080
+ }
1081
+ global_stats().IncrementHttp2MetadataSize(frame_length_);
1082
+ }
915
1083
  if (input.eof_error()) {
916
1084
  if (GPR_UNLIKELY(is_last && is_boundary())) {
917
- return GRPC_ERROR_CREATE(
1085
+ auto err = input.TakeError();
1086
+ if (!err.ok() && !IsStreamError(err)) return err;
1087
+ return absl::InternalError(
918
1088
  "Incomplete header at the end of a header/continuation sequence");
919
1089
  }
920
1090
  unparsed_bytes_ = std::vector<uint8_t>(input.frontier(), input.end_ptr());
921
- return absl::OkStatus();
1091
+ return input.TakeError();
922
1092
  }
923
1093
  return input.TakeError();
924
1094
  }
925
1095
 
926
- bool HPackParser::ParseInputInner(Input* input) {
1096
+ void HPackParser::ParseInputInner(Input* input) {
927
1097
  switch (priority_) {
928
1098
  case Priority::None:
929
1099
  break;
930
1100
  case Priority::Included: {
931
- if (input->remaining() < 5) return input->UnexpectedEOF(false);
1101
+ if (input->remaining() < 5) {
1102
+ input->UnexpectedEOF();
1103
+ return;
1104
+ }
932
1105
  input->Advance(5);
933
1106
  input->UpdateFrontier();
934
1107
  priority_ = Priority::None;
935
1108
  }
936
1109
  }
937
1110
  while (!input->end_of_stream()) {
938
- if (GPR_UNLIKELY(!Parser(input, metadata_buffer_, metadata_size_limit_,
939
- &table_, &dynamic_table_updates_allowed_,
940
- &frame_length_, log_info_)
1111
+ if (GPR_UNLIKELY(!Parser(input, metadata_buffer_, &table_,
1112
+ &dynamic_table_updates_allowed_, &frame_length_,
1113
+ &metadata_early_detection_, log_info_)
941
1114
  .Parse())) {
942
- return false;
1115
+ return;
943
1116
  }
944
1117
  input->UpdateFrontier();
945
1118
  }
946
- return true;
947
1119
  }
948
1120
 
949
1121
  void HPackParser::FinishFrame() { metadata_buffer_ = nullptr; }
950
1122
 
1123
+ void HPackParser::HandleMetadataSoftSizeLimitExceeded(Input* input) {
1124
+ // Collect a summary of sizes so far for debugging
1125
+ // Do not collect contents, for fear of exposing PII.
1126
+ std::string summary;
1127
+ std::string error_message;
1128
+ if (metadata_buffer_ != nullptr) {
1129
+ MetadataSizeLimitExceededEncoder encoder(summary);
1130
+ metadata_buffer_->Encode(&encoder);
1131
+ }
1132
+ error_message = absl::StrCat(
1133
+ "received metadata size exceeds soft limit (", frame_length_, " vs. ",
1134
+ metadata_early_detection_.soft_limit(),
1135
+ "), rejecting requests with some random probability", summary);
1136
+ if (metadata_buffer_ != nullptr) {
1137
+ metadata_buffer_->Clear();
1138
+ metadata_buffer_ = nullptr;
1139
+ }
1140
+ input->SetErrorAndContinueParsing(
1141
+ absl::ResourceExhaustedError(error_message));
1142
+ }
1143
+
951
1144
  } // namespace grpc_core