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 <grpc/support/port_platform.h>
16
17 #include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
18
19 #include <stddef.h>
20
21 #include <initializer_list>
22
23 #include "absl/strings/str_format.h"
24
25 #include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
26 #include "src/core/lib/gprpp/status_helper.h"
27 #include "src/core/lib/iomgr/error.h"
28 #include "src/core/lib/slice/slice.h"
29
30 namespace grpc_core {
31
32 namespace {
33 class MetadataSizeLimitExceededEncoder {
34 public:
MetadataSizeLimitExceededEncoder(std::string & summary)35 explicit MetadataSizeLimitExceededEncoder(std::string& summary)
36 : summary_(summary) {}
37
Encode(const Slice & key,const Slice & value)38 void Encode(const Slice& key, const Slice& value) {
39 AddToSummary(key.as_string_view(), value.size());
40 }
41
42 template <typename Key, typename Value>
Encode(Key,const Value & value)43 void Encode(Key, const Value& value) {
44 AddToSummary(Key::key(), EncodedSizeOfKey(Key(), value));
45 }
46
47 private:
AddToSummary(absl::string_view key,size_t value_length)48 void AddToSummary(absl::string_view key,
49 size_t value_length) GPR_ATTRIBUTE_NOINLINE {
50 absl::StrAppend(&summary_, " ", key, ":",
51 hpack_constants::SizeForEntry(key.size(), value_length),
52 "B");
53 }
54 std::string& summary_;
55 };
56
MakeStreamError(absl::Status error)57 absl::Status MakeStreamError(absl::Status error) {
58 GPR_DEBUG_ASSERT(!error.ok());
59 return grpc_error_set_int(std::move(error), StatusIntProperty::kStreamId, 0);
60 }
61 } // namespace
62
Materialize() const63 absl::Status HpackParseResult::Materialize() const {
64 if (materialized_status_.has_value()) return *materialized_status_;
65 materialized_status_ = BuildMaterialized();
66 return *materialized_status_;
67 }
68
BuildMaterialized() const69 absl::Status HpackParseResult::BuildMaterialized() const {
70 switch (status_.get()) {
71 case HpackParseStatus::kOk:
72 return absl::OkStatus();
73 case HpackParseStatus::kEof:
74 Crash("Materialize() called on EOF");
75 break;
76 case HpackParseStatus::kMovedFrom:
77 Crash("Materialize() called on moved-from object");
78 break;
79 case HpackParseStatus::kInvalidMetadata:
80 if (key_.empty()) {
81 return MakeStreamError(absl::InternalError(
82 ValidateMetadataResultToString(validate_metadata_result_)));
83 } else {
84 return MakeStreamError(absl::InternalError(absl::StrCat(
85 ValidateMetadataResultToString(validate_metadata_result_), ": ",
86 key_)));
87 }
88 case HpackParseStatus::kSoftMetadataLimitExceeded:
89 case HpackParseStatus::kHardMetadataLimitExceeded: {
90 const auto& e = metadata_limit_exceeded_;
91 // Collect a summary of sizes so far for debugging
92 // Do not collect contents, for fear of exposing PII.
93 std::string summary;
94 if (e.prior != nullptr) {
95 MetadataSizeLimitExceededEncoder encoder(summary);
96 e.prior->Encode(&encoder);
97 }
98 return MakeStreamError(absl::ResourceExhaustedError(absl::StrCat(
99 "received metadata size exceeds ",
100 status_.get() == HpackParseStatus::kSoftMetadataLimitExceeded
101 ? "soft"
102 : "hard",
103 " limit (", e.frame_length, " vs. ", e.limit, ")",
104 summary.empty() ? "" : "; ", summary)));
105 }
106 case HpackParseStatus::kHardMetadataLimitExceededByKey: {
107 const auto& e = metadata_limit_exceeded_by_atom_;
108 return MakeStreamError(absl::ResourceExhaustedError(
109 absl::StrCat("received metadata size exceeds hard limit (key length ",
110 e.atom_length, " vs. ", e.limit, ")")));
111 }
112 case HpackParseStatus::kHardMetadataLimitExceededByValue: {
113 const auto& e = metadata_limit_exceeded_by_atom_;
114 return MakeStreamError(absl::ResourceExhaustedError(absl::StrCat(
115 "received metadata size exceeds hard limit (value length ",
116 e.atom_length, " vs. ", e.limit, ")")));
117 }
118 case HpackParseStatus::kMetadataParseError:
119 if (!key_.empty()) {
120 return MakeStreamError(absl::InternalError(
121 absl::StrCat("Error parsing '", key_, "' metadata")));
122 } else {
123 return MakeStreamError(absl::InternalError("Error parsing metadata"));
124 }
125 case HpackParseStatus::kUnbase64Failed:
126 if (!key_.empty()) {
127 return MakeStreamError(absl::InternalError(absl::StrCat(
128 "Error parsing '", key_, "' metadata: illegal base64 encoding")));
129 } else {
130 return MakeStreamError(absl::InternalError(
131 absl::StrCat("Failed base64 decoding metadata")));
132 }
133 case HpackParseStatus::kIncompleteHeaderAtBoundary:
134 return absl::InternalError(
135 "Incomplete header at the end of a header/continuation sequence");
136 case HpackParseStatus::kVarintOutOfRange:
137 return absl::InternalError(absl::StrFormat(
138 "integer overflow in hpack integer decoding: have 0x%08x, "
139 "got byte 0x%02x",
140 varint_out_of_range_.value, varint_out_of_range_.last_byte));
141 case HpackParseStatus::kIllegalTableSizeChange:
142 return absl::InternalError(absl::StrCat(
143 "Attempt to make hpack table ", illegal_table_size_change_.new_size,
144 " bytes when max is ", illegal_table_size_change_.max_size,
145 " bytes"));
146 case HpackParseStatus::kAddBeforeTableSizeUpdated:
147 return absl::InternalError(
148 absl::StrCat("HPACK max table size reduced to ",
149 illegal_table_size_change_.new_size,
150 " but not reflected by hpack stream (still at ",
151 illegal_table_size_change_.max_size, ")"));
152 case HpackParseStatus::kParseHuffFailed:
153 if (!key_.empty()) {
154 return absl::InternalError(
155 absl::StrCat("Failed huffman decoding '", key_, "' metadata"));
156 } else {
157 return absl::InternalError(
158 absl::StrCat("Failed huffman decoding metadata"));
159 }
160 break;
161 case HpackParseStatus::kTooManyDynamicTableSizeChanges:
162 return absl::InternalError(
163 "More than two max table size changes in a single frame");
164 case HpackParseStatus::kMaliciousVarintEncoding:
165 return absl::InternalError(
166 "Malicious varint encoding detected in HPACK stream");
167 case HpackParseStatus::kInvalidHpackIndex:
168 return absl::InternalError(absl::StrFormat(
169 "Invalid HPACK index received (%d)", invalid_hpack_index_));
170 case HpackParseStatus::kIllegalHpackOpCode:
171 return absl::InternalError("Illegal hpack op code");
172 }
173 GPR_UNREACHABLE_CODE(return absl::UnknownError("Should never reach here"));
174 }
175
176 } // namespace grpc_core
177