• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
16 
17 #include <grpc/support/port_platform.h>
18 #include <stddef.h>
19 
20 #include "absl/log/check.h"
21 #include "absl/strings/str_format.h"
22 #include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
23 #include "src/core/lib/iomgr/error.h"
24 #include "src/core/lib/slice/slice.h"
25 #include "src/core/util/status_helper.h"
26 
27 namespace grpc_core {
28 
29 namespace {
30 class MetadataSizeLimitExceededEncoder {
31  public:
MetadataSizeLimitExceededEncoder(std::string & summary)32   explicit MetadataSizeLimitExceededEncoder(std::string& summary)
33       : summary_(summary) {}
34 
Encode(const Slice & key,const Slice & value)35   void Encode(const Slice& key, const Slice& value) {
36     AddToSummary(key.as_string_view(), value.size());
37   }
38 
39   template <typename Key, typename Value>
Encode(Key,const Value & value)40   void Encode(Key, const Value& value) {
41     AddToSummary(Key::key(), EncodedSizeOfKey(Key(), value));
42   }
43 
44  private:
AddToSummary(absl::string_view key,size_t value_length)45   void AddToSummary(absl::string_view key,
46                     size_t value_length) GPR_ATTRIBUTE_NOINLINE {
47     absl::StrAppend(&summary_, " ", key, ":",
48                     hpack_constants::SizeForEntry(key.size(), value_length),
49                     "B");
50   }
51   std::string& summary_;
52 };
53 
MakeStreamError(absl::Status error)54 absl::Status MakeStreamError(absl::Status error) {
55   DCHECK(!error.ok());
56   return grpc_error_set_int(std::move(error), StatusIntProperty::kStreamId, 0);
57 }
58 }  // namespace
59 
Materialize() const60 absl::Status HpackParseResult::Materialize() const {
61   if (state_ != nullptr && state_->materialized_status.has_value()) {
62     return *state_->materialized_status;
63   }
64   absl::Status materialized_status = BuildMaterialized();
65   if (!materialized_status.ok()) {
66     // We can safely assume state_ is not null here, since BuildMaterialized
67     // returns ok if it is.
68     state_->materialized_status = materialized_status;
69   }
70   return materialized_status;
71 }
72 
BuildMaterialized() const73 absl::Status HpackParseResult::BuildMaterialized() const {
74   if (state_ == nullptr) return absl::OkStatus();
75   switch (state_->status.get()) {
76     case HpackParseStatus::kOk:
77       return absl::OkStatus();
78     case HpackParseStatus::kEof:
79       Crash("Materialize() called on EOF");
80       break;
81     case HpackParseStatus::kMovedFrom:
82       Crash("Materialize() called on moved-from object");
83       break;
84     case HpackParseStatus::kInvalidMetadata:
85       if (state_->key.empty()) {
86         return MakeStreamError(absl::InternalError(
87             ValidateMetadataResultToString(state_->validate_metadata_result)));
88       } else {
89         return MakeStreamError(absl::InternalError(absl::StrCat(
90             ValidateMetadataResultToString(state_->validate_metadata_result),
91             ": ", state_->key)));
92       }
93     case HpackParseStatus::kSoftMetadataLimitExceeded:
94     case HpackParseStatus::kHardMetadataLimitExceeded: {
95       const auto& e = state_->metadata_limit_exceeded;
96       // Collect a summary of sizes so far for debugging
97       // Do not collect contents, for fear of exposing PII.
98       std::string summary;
99       if (e.prior != nullptr) {
100         MetadataSizeLimitExceededEncoder encoder(summary);
101         e.prior->Encode(&encoder);
102       }
103       return MakeStreamError(absl::ResourceExhaustedError(absl::StrCat(
104           "received metadata size exceeds ",
105           state_->status.get() == HpackParseStatus::kSoftMetadataLimitExceeded
106               ? "soft"
107               : "hard",
108           " limit (", e.frame_length, " vs. ", e.limit, ")",
109           summary.empty() ? "" : "; ", summary)));
110     }
111     case HpackParseStatus::kHardMetadataLimitExceededByKey: {
112       const auto& e = state_->metadata_limit_exceeded_by_atom;
113       return MakeStreamError(absl::ResourceExhaustedError(
114           absl::StrCat("received metadata size exceeds hard limit (key length ",
115                        e.atom_length, " vs. ", e.limit, ")")));
116     }
117     case HpackParseStatus::kHardMetadataLimitExceededByValue: {
118       const auto& e = state_->metadata_limit_exceeded_by_atom;
119       return MakeStreamError(absl::ResourceExhaustedError(absl::StrCat(
120           "received metadata size exceeds hard limit (value length ",
121           e.atom_length, " vs. ", e.limit, ")")));
122     }
123     case HpackParseStatus::kMetadataParseError:
124       if (!state_->key.empty()) {
125         return MakeStreamError(absl::InternalError(
126             absl::StrCat("Error parsing '", state_->key, "' metadata")));
127       } else {
128         return MakeStreamError(absl::InternalError("Error parsing metadata"));
129       }
130     case HpackParseStatus::kUnbase64Failed:
131       if (!state_->key.empty()) {
132         return MakeStreamError(absl::InternalError(
133             absl::StrCat("Error parsing '", state_->key,
134                          "' metadata: illegal base64 encoding")));
135       } else {
136         return MakeStreamError(absl::InternalError(
137             absl::StrCat("Failed base64 decoding metadata")));
138       }
139     case HpackParseStatus::kIncompleteHeaderAtBoundary:
140       return absl::InternalError(
141           "Incomplete header at the end of a header/continuation sequence");
142     case HpackParseStatus::kVarintOutOfRange:
143       return absl::InternalError(absl::StrFormat(
144           "integer overflow in hpack integer decoding: have 0x%08x, "
145           "got byte 0x%02x",
146           state_->varint_out_of_range.value,
147           state_->varint_out_of_range.last_byte));
148     case HpackParseStatus::kIllegalTableSizeChange:
149       return absl::InternalError(absl::StrCat(
150           "Attempt to make hpack table ",
151           state_->illegal_table_size_change.new_size, " bytes when max is ",
152           state_->illegal_table_size_change.max_size, " bytes"));
153     case HpackParseStatus::kAddBeforeTableSizeUpdated:
154       return absl::InternalError(
155           absl::StrCat("HPACK max table size reduced to ",
156                        state_->illegal_table_size_change.new_size,
157                        " but not reflected by hpack stream (still at ",
158                        state_->illegal_table_size_change.max_size, ")"));
159     case HpackParseStatus::kParseHuffFailed:
160       if (!state_->key.empty()) {
161         return absl::InternalError(absl::StrCat("Failed huffman decoding '",
162                                                 state_->key, "' metadata"));
163       } else {
164         return absl::InternalError(
165             absl::StrCat("Failed huffman decoding metadata"));
166       }
167       break;
168     case HpackParseStatus::kTooManyDynamicTableSizeChanges:
169       return absl::InternalError(
170           "More than two max table size changes in a single frame");
171     case HpackParseStatus::kMaliciousVarintEncoding:
172       return absl::InternalError(
173           "Malicious varint encoding detected in HPACK stream");
174     case HpackParseStatus::kInvalidHpackIndex:
175       return absl::InternalError(absl::StrFormat(
176           "Invalid HPACK index received (%d)", state_->invalid_hpack_index));
177     case HpackParseStatus::kIllegalHpackOpCode:
178       return absl::InternalError("Illegal hpack op code");
179   }
180   GPR_UNREACHABLE_CODE(return absl::UnknownError("Should never reach here"));
181 }
182 
183 }  // namespace grpc_core
184