grpc 1.54.0 → 1.54.2

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.

@@ -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,25 +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
632
  Parser(Input* input, grpc_metadata_batch* metadata_buffer, HPackTable* table,
526
633
  uint8_t* dynamic_table_updates_allowed, uint32_t* frame_length,
527
- RandomEarlyDetection* metadata_early_detection, bool is_last,
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
640
  metadata_early_detection_(metadata_early_detection),
535
- is_last_(is_last),
536
641
  log_info_(log_info) {}
537
642
 
538
643
  // Skip any priority bits, or return false on failure
539
644
  bool SkipPriority() {
540
- if (input_->remaining() < 5) return input_->UnexpectedEOF(false);
645
+ if (input_->remaining() < 5) {
646
+ input_->UnexpectedEOF();
647
+ return false;
648
+ }
541
649
  input_->Advance(5);
542
650
  return true;
543
651
  }
@@ -610,8 +718,9 @@ class HPackParser::Parser {
610
718
  case 8:
611
719
  if (cur == 0x80) {
612
720
  // illegal value.
613
- return input_->MaybeSetErrorAndReturn(
614
- [] { return GRPC_ERROR_CREATE("Illegal hpack op code"); }, false);
721
+ input_->SetErrorAndStopParsing(
722
+ absl::InternalError("Illegal hpack op code"));
723
+ return false;
615
724
  }
616
725
  ABSL_FALLTHROUGH_INTENDED;
617
726
  case 9:
@@ -648,24 +757,31 @@ class HPackParser::Parser {
648
757
  type = "???";
649
758
  break;
650
759
  }
651
- gpr_log(GPR_DEBUG, "HTTP:%d:%s:%s: %s", log_info_.stream_id, type,
652
- 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());
653
768
  }
654
769
 
655
- bool EmitHeader(const HPackTable::Memento& md) {
770
+ void EmitHeader(const HPackTable::Memento& md) {
656
771
  // Pass up to the transport
657
- if (GPR_UNLIKELY(metadata_buffer_ == nullptr)) return true;
658
- *frame_length_ += md.transport_size();
659
- if (metadata_early_detection_->MustReject(*frame_length_)) {
772
+ *frame_length_ += md.md.transport_size();
773
+ if (!input_->has_error() &&
774
+ metadata_early_detection_->MustReject(*frame_length_)) {
660
775
  // Reject any requests above hard metadata limit.
661
- return HandleMetadataSizeLimitExceeded(md, /*exceeded_hard_limit=*/true);
662
- } else if (is_last_ && metadata_early_detection_->Reject(*frame_length_)) {
663
- // Reject some random sample of requests above soft metadata limit.
664
- return HandleMetadataSizeLimitExceeded(md, /*exceeded_hard_limit=*/false);
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);
665
784
  }
666
-
667
- metadata_buffer_->Set(md);
668
- return true;
669
785
  }
670
786
 
671
787
  bool FinishHeaderAndAddToTable(absl::optional<HPackTable::Memento> md) {
@@ -676,73 +792,149 @@ class HPackParser::Parser {
676
792
  LogHeader(*md);
677
793
  }
678
794
  // Emit whilst we own the metadata.
679
- auto r = EmitHeader(*md);
795
+ EmitHeader(*md);
680
796
  // Add to the hpack table
681
797
  grpc_error_handle err = table_->Add(std::move(*md));
682
798
  if (GPR_UNLIKELY(!err.ok())) {
683
- input_->SetError(err);
799
+ input_->SetErrorAndStopParsing(std::move(err));
684
800
  return false;
685
801
  };
686
- return r;
802
+ return true;
687
803
  }
688
804
 
689
805
  bool FinishHeaderOmitFromTable(absl::optional<HPackTable::Memento> md) {
690
806
  // Allow higher code to just pass in failures ... simplifies things a bit.
691
807
  if (!md.has_value()) return false;
692
- return FinishHeaderOmitFromTable(*md);
808
+ FinishHeaderOmitFromTable(*md);
809
+ return true;
693
810
  }
694
811
 
695
- bool FinishHeaderOmitFromTable(const HPackTable::Memento& md) {
812
+ void FinishHeaderOmitFromTable(const HPackTable::Memento& md) {
696
813
  // Log if desired
697
814
  if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_chttp2_hpack_parser)) {
698
815
  LogHeader(md);
699
816
  }
700
- return EmitHeader(md);
817
+ EmitHeader(md);
701
818
  }
702
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
+
703
877
  // Parse a string encoded key and a string encoded value
704
878
  absl::optional<HPackTable::Memento> ParseLiteralKey() {
705
879
  auto key = String::Parse(input_);
706
- if (!key.has_value()) return {};
707
- auto value = ParseValueString(absl::EndsWith(key->string_view(), "-bin"));
708
- if (GPR_UNLIKELY(!value.has_value())) {
709
- 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;
710
892
  }
711
- auto key_string = key->string_view();
712
- auto value_slice = value->Take();
713
- const auto transport_size = key_string.size() + value_slice.size() +
714
- hpack_constants::kEntryOverhead;
715
- return grpc_metadata_batch::Parse(
716
- key->string_view(), std::move(value_slice), transport_size,
717
- [key_string](absl::string_view error, const Slice& value) {
718
- ReportMetadataParseError(key_string, error, value.as_string_view());
719
- });
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);
720
913
  }
721
914
 
722
915
  // Parse an index encoded key and a string encoded value
723
916
  absl::optional<HPackTable::Memento> ParseIdxKey(uint32_t index) {
724
917
  const auto* elem = table_->Lookup(index);
725
918
  if (GPR_UNLIKELY(elem == nullptr)) {
726
- return InvalidHPackIndexError(index,
727
- absl::optional<HPackTable::Memento>());
728
- }
729
- auto value = ParseValueString(elem->is_binary_header());
730
- if (GPR_UNLIKELY(!value.has_value())) return {};
731
- return elem->WithNewValue(
732
- value->Take(), [=](absl::string_view error, const Slice& value) {
733
- ReportMetadataParseError(elem->key(), error, value.as_string_view());
734
- });
735
- }
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
+ };
736
928
 
737
929
  // Parse a varint index encoded key and a string encoded value
738
930
  absl::optional<HPackTable::Memento> ParseVarIdxKey(uint32_t offset) {
739
931
  auto index = input_->ParseVarint(offset);
740
- if (GPR_UNLIKELY(!index.has_value())) return {};
932
+ if (GPR_UNLIKELY(!index.has_value())) return absl::nullopt;
741
933
  return ParseIdxKey(*index);
742
934
  }
743
935
 
744
936
  // Parse a string, figuring out if it's binary or not by the key name.
745
- absl::optional<String> ParseValueString(bool is_binary) {
937
+ String::ParseResult ParseValueString(bool is_binary) {
746
938
  if (is_binary) {
747
939
  return String::ParseBinary(input_);
748
940
  } else {
@@ -756,26 +948,25 @@ class HPackParser::Parser {
756
948
  if (!index.has_value()) return false;
757
949
  const auto* elem = table_->Lookup(*index);
758
950
  if (GPR_UNLIKELY(elem == nullptr)) {
759
- return InvalidHPackIndexError(*index, false);
951
+ InvalidHPackIndexError(*index);
952
+ return false;
760
953
  }
761
- return FinishHeaderOmitFromTable(*elem);
954
+ FinishHeaderOmitFromTable(*elem);
955
+ return true;
762
956
  }
763
957
 
764
958
  // finish parsing a max table size change
765
959
  bool FinishMaxTableSize(absl::optional<uint32_t> size) {
766
960
  if (!size.has_value()) return false;
767
961
  if (*dynamic_table_updates_allowed_ == 0) {
768
- return input_->MaybeSetErrorAndReturn(
769
- [] {
770
- return GRPC_ERROR_CREATE(
771
- "More than two max table size changes in a single frame");
772
- },
773
- false);
962
+ input_->SetErrorAndStopParsing(absl::InternalError(
963
+ "More than two max table size changes in a single frame"));
964
+ return false;
774
965
  }
775
966
  (*dynamic_table_updates_allowed_)--;
776
967
  grpc_error_handle err = table_->SetCurrentTableSize(*size);
777
968
  if (!err.ok()) {
778
- input_->SetError(err);
969
+ input_->SetErrorAndStopParsing(std::move(err));
779
970
  return false;
780
971
  }
781
972
  return true;
@@ -783,47 +974,28 @@ class HPackParser::Parser {
783
974
 
784
975
  // Set an invalid hpack index error if no error has been set. Returns result
785
976
  // unmodified.
786
- template <typename R>
787
- R InvalidHPackIndexError(uint32_t index, R result) {
788
- return input_->MaybeSetErrorAndReturn(
789
- [this, index] {
790
- return grpc_error_set_int(
791
- grpc_error_set_int(
792
- GRPC_ERROR_CREATE("Invalid HPACK index received"),
793
- StatusIntProperty::kIndex, static_cast<intptr_t>(index)),
794
- StatusIntProperty::kSize,
795
- static_cast<intptr_t>(this->table_->num_entries()));
796
- },
797
- std::move(result));
798
- }
799
-
800
- class MetadataSizeLimitExceededEncoder {
801
- public:
802
- explicit MetadataSizeLimitExceededEncoder(std::string& summary)
803
- : summary_(summary) {}
804
-
805
- void Encode(const Slice& key, const Slice& value) {
806
- AddToSummary(key.as_string_view(), value.size());
807
- }
808
-
809
- template <typename Key, typename Value>
810
- void Encode(Key, const Value& value) {
811
- AddToSummary(Key::key(), EncodedSizeOfKey(Key(), value));
812
- }
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
+ }
813
985
 
814
- private:
815
- void AddToSummary(absl::string_view key,
816
- size_t value_length) GPR_ATTRIBUTE_NOINLINE {
817
- absl::StrAppend(&summary_, " ", key, ":",
818
- hpack_constants::SizeForEntry(key.size(), value_length),
819
- "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;
820
991
  }
821
- std::string& summary_;
822
- };
992
+ // StreamId is used as a signal to skip this stream but keep the connection
993
+ // alive
994
+ input_->SetErrorAndContinueParsing(status);
995
+ }
823
996
 
824
997
  GPR_ATTRIBUTE_NOINLINE
825
- bool HandleMetadataSizeLimitExceeded(const HPackTable::Memento& md,
826
- bool exceeded_hard_limit) {
998
+ void HandleMetadataHardSizeLimitExceeded(const HPackTable::Memento& md) {
827
999
  // Collect a summary of sizes so far for debugging
828
1000
  // Do not collect contents, for fear of exposing PII.
829
1001
  std::string summary;
@@ -832,49 +1004,22 @@ class HPackParser::Parser {
832
1004
  MetadataSizeLimitExceededEncoder encoder(summary);
833
1005
  metadata_buffer_->Encode(&encoder);
834
1006
  }
835
- summary =
836
- absl::StrCat("; adding ", md.key(), " (length ", md.transport_size(),
837
- "B)", summary.empty() ? "" : " to ", summary);
838
- if (exceeded_hard_limit) {
839
- error_message = absl::StrCat(
840
- "received initial metadata size exceeds hard limit (", *frame_length_,
841
- " vs. ", metadata_early_detection_->hard_limit(), ")", summary);
842
- } else {
843
- error_message = absl::StrCat(
844
- "received initial metadata size exceeds soft limit (", *frame_length_,
845
- " vs. ", metadata_early_detection_->soft_limit(),
846
- "), rejecting requests with some random probability", summary);
847
- }
848
- if (metadata_buffer_ != nullptr) metadata_buffer_->Clear();
849
- // StreamId is used as a signal to skip this stream but keep the connection
850
- // alive
851
- return input_->MaybeSetErrorAndReturn(
852
- [error_message = std::move(error_message)] {
853
- return grpc_error_set_int(
854
- grpc_error_set_int(GRPC_ERROR_CREATE(error_message),
855
- StatusIntProperty::kRpcStatus,
856
- GRPC_STATUS_RESOURCE_EXHAUSTED),
857
- StatusIntProperty::kStreamId, 0);
858
- },
859
- false);
860
- }
861
-
862
- static void ReportMetadataParseError(absl::string_view key,
863
- absl::string_view error,
864
- absl::string_view value) {
865
- gpr_log(
866
- GPR_ERROR, "Error parsing metadata: %s",
867
- 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));
868
1014
  }
869
1015
 
870
1016
  Input* const input_;
871
- grpc_metadata_batch* const metadata_buffer_;
1017
+ grpc_metadata_batch* metadata_buffer_;
872
1018
  HPackTable* const table_;
873
1019
  uint8_t* const dynamic_table_updates_allowed_;
874
1020
  uint32_t* const frame_length_;
875
1021
  // Random early detection of metadata size limits.
876
1022
  RandomEarlyDetection* metadata_early_detection_;
877
- bool is_last_; // Whether this is the last frame.
878
1023
  const LogInfo log_info_;
879
1024
  };
880
1025
 
@@ -928,26 +1073,35 @@ grpc_error_handle HPackParser::Parse(const grpc_slice& slice, bool is_last) {
928
1073
  }
929
1074
 
930
1075
  grpc_error_handle HPackParser::ParseInput(Input input, bool is_last) {
931
- bool parsed_ok = ParseInputInner(&input, is_last);
932
- if (is_last) global_stats().IncrementHttp2MetadataSize(frame_length_);
933
- 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
+ }
934
1083
  if (input.eof_error()) {
935
1084
  if (GPR_UNLIKELY(is_last && is_boundary())) {
936
- return GRPC_ERROR_CREATE(
1085
+ auto err = input.TakeError();
1086
+ if (!err.ok() && !IsStreamError(err)) return err;
1087
+ return absl::InternalError(
937
1088
  "Incomplete header at the end of a header/continuation sequence");
938
1089
  }
939
1090
  unparsed_bytes_ = std::vector<uint8_t>(input.frontier(), input.end_ptr());
940
- return absl::OkStatus();
1091
+ return input.TakeError();
941
1092
  }
942
1093
  return input.TakeError();
943
1094
  }
944
1095
 
945
- bool HPackParser::ParseInputInner(Input* input, bool is_last) {
1096
+ void HPackParser::ParseInputInner(Input* input) {
946
1097
  switch (priority_) {
947
1098
  case Priority::None:
948
1099
  break;
949
1100
  case Priority::Included: {
950
- if (input->remaining() < 5) return input->UnexpectedEOF(false);
1101
+ if (input->remaining() < 5) {
1102
+ input->UnexpectedEOF();
1103
+ return;
1104
+ }
951
1105
  input->Advance(5);
952
1106
  input->UpdateFrontier();
953
1107
  priority_ = Priority::None;
@@ -956,15 +1110,35 @@ bool HPackParser::ParseInputInner(Input* input, bool is_last) {
956
1110
  while (!input->end_of_stream()) {
957
1111
  if (GPR_UNLIKELY(!Parser(input, metadata_buffer_, &table_,
958
1112
  &dynamic_table_updates_allowed_, &frame_length_,
959
- &metadata_early_detection_, is_last, log_info_)
1113
+ &metadata_early_detection_, log_info_)
960
1114
  .Parse())) {
961
- return false;
1115
+ return;
962
1116
  }
963
1117
  input->UpdateFrontier();
964
1118
  }
965
- return true;
966
1119
  }
967
1120
 
968
1121
  void HPackParser::FinishFrame() { metadata_buffer_ = nullptr; }
969
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
+
970
1144
  } // namespace grpc_core