summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibo Chen <libo.chen.cn@windriver.com>2024-12-06 12:00:41 +0800
committerArmin Kuster <akuster808@gmail.com>2024-12-15 14:05:17 -0500
commit85d783a457573878fdda93911f519cfb01cfcda3 (patch)
tree2cc5d7a7338aff102b544f0ce72e62468a097be2
parent2ee358a0654f745bf2c85f436ffd6d925b8a2cf2 (diff)
downloadmeta-openembedded-85d783a457573878fdda93911f519cfb01cfcda3.tar.gz
grpc: Fix CVE-2024-7246
Backport patches [1] to fix CVE-2024-7246. [1] https://github.com/grpc/grpc/pull/37361/files Signed-off-by: Libo Chen <libo.chen.cn@windriver.com> Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r--meta-oe/recipes-devtools/grpc/grpc/CVE-2024-7246.patch420
-rw-r--r--meta-oe/recipes-devtools/grpc/grpc_1.60.1.bb1
2 files changed, 421 insertions, 0 deletions
diff --git a/meta-oe/recipes-devtools/grpc/grpc/CVE-2024-7246.patch b/meta-oe/recipes-devtools/grpc/grpc/CVE-2024-7246.patch
new file mode 100644
index 0000000000..a690b49c67
--- /dev/null
+++ b/meta-oe/recipes-devtools/grpc/grpc/CVE-2024-7246.patch
@@ -0,0 +1,420 @@
1From ac0948e39eb19c3325a576e152709d4cc915a7e4 Mon Sep 17 00:00:00 2001
2From: Craig Tiller <ctiller@google.com>
3Date: Thu, 1 Aug 2024 13:02:27 -0700
4Subject: [PATCH] [v1.60] [chttp2] Fix a bug in hpack error handling (#37361)
5
6PiperOrigin-RevId: 657234128
7PiperOrigin-RevId: 658458047
8
9Craig Tiller <ctiller@google.com>
10
11CVE: CVE-2024-7246
12
13Upstream-Status: Backport
14[https://github.com/grpc/grpc/pull/37361/files]
15
16Signed-off-by: Libo Chen <libo.chen.cn@windriver.com>
17---
18 .../chttp2/transport/hpack_parser.cc | 63 +++++++------
19 .../transport/chttp2/transport/hpack_parser.h | 2 +
20 .../transport/chttp2/hpack_parser_test.cc | 89 ++++++++++++++++---
21 .../transport/chttp2/hpack_sync_fuzzer.cc | 62 +++++++++++++
22 .../transport/chttp2/hpack_sync_fuzzer.proto | 3 +
23 5 files changed, 179 insertions(+), 40 deletions(-)
24
25diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
26index 31bf464..f2fe80c 100644
27--- a/src/core/ext/transport/chttp2/transport/hpack_parser.cc
28+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
29@@ -91,12 +91,14 @@ constexpr Base64InverseTable kBase64InverseTable;
30 class HPackParser::Input {
31 public:
32 Input(grpc_slice_refcount* current_slice_refcount, const uint8_t* begin,
33- const uint8_t* end, absl::BitGenRef bitsrc, HpackParseResult& error)
34+ const uint8_t* end, absl::BitGenRef bitsrc,
35+ HpackParseResult& frame_error, HpackParseResult& field_error)
36 : current_slice_refcount_(current_slice_refcount),
37 begin_(begin),
38 end_(end),
39 frontier_(begin),
40- error_(error),
41+ frame_error_(frame_error),
42+ field_error_(field_error),
43 bitsrc_(bitsrc) {}
44
45 // If input is backed by a slice, retrieve its refcount. If not, return
46@@ -215,14 +217,18 @@ class HPackParser::Input {
47
48 // Check if we saw an EOF
49 bool eof_error() const {
50- return min_progress_size_ != 0 || error_.connection_error();
51+ return min_progress_size_ != 0 || frame_error_.connection_error();
52+ }
53+
54+ // Reset the field error to be ok
55+ void ClearFieldError() {
56+ if (field_error_.ok()) return;
57+ field_error_ = HpackParseResult();
58 }
59
60 // Minimum number of bytes to unstuck the current parse
61 size_t min_progress_size() const { return min_progress_size_; }
62
63- bool has_error() const { return !error_.ok(); }
64-
65 // Set the current error - tweaks the error to include a stream id so that
66 // chttp2 does not close the connection.
67 // Intended for errors that are specific to a stream and recoverable.
68@@ -246,10 +252,7 @@ class HPackParser::Input {
69 // read prior to being able to get further in this parse.
70 void UnexpectedEOF(size_t min_progress_size) {
71 GPR_ASSERT(min_progress_size > 0);
72- if (min_progress_size_ != 0 || error_.connection_error()) {
73- GPR_DEBUG_ASSERT(eof_error());
74- return;
75- }
76+ if (eof_error()) return;
77 // Set min progress size, taking into account bytes parsed already but not
78 // consumed.
79 min_progress_size_ = min_progress_size + (begin_ - frontier_);
80@@ -302,13 +305,18 @@ class HPackParser::Input {
81 // Do not use this directly, instead use SetErrorAndContinueParsing or
82 // SetErrorAndStopParsing.
83 void SetError(HpackParseResult error) {
84- if (!error_.ok() || min_progress_size_ > 0) {
85- if (error.connection_error() && !error_.connection_error()) {
86- error_ = std::move(error); // connection errors dominate
87+ SetErrorFor(frame_error_, error);
88+ SetErrorFor(field_error_, std::move(error));
89+ }
90+
91+ void SetErrorFor(HpackParseResult& error, HpackParseResult new_error) {
92+ if (!error.ok() || min_progress_size_ > 0) {
93+ if (new_error.connection_error() && !error.connection_error()) {
94+ error = std::move(new_error); // connection errors dominate
95 }
96 return;
97 }
98- error_ = std::move(error);
99+ error = std::move(new_error);
100 }
101
102 // Refcount if we are backed by a slice
103@@ -320,7 +328,8 @@ class HPackParser::Input {
104 // Frontier denotes the first byte past successfully processed input
105 const uint8_t* frontier_;
106 // Current error
107- HpackParseResult& error_;
108+ HpackParseResult& frame_error_;
109+ HpackParseResult& field_error_;
110 // If the error was EOF, we flag it here by noting how many more bytes would
111 // be needed to make progress
112 size_t min_progress_size_ = 0;
113@@ -597,6 +606,7 @@ class HPackParser::Parser {
114 bool ParseTop() {
115 GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kTop);
116 auto cur = *input_->Next();
117+ input_->ClearFieldError();
118 switch (cur >> 4) {
119 // Literal header not indexed - First byte format: 0000xxxx
120 // Literal header never indexed - First byte format: 0001xxxx
121@@ -702,7 +712,7 @@ class HPackParser::Parser {
122 break;
123 }
124 gpr_log(
125- GPR_DEBUG, "HTTP:%d:%s:%s: %s%s", log_info_.stream_id, type,
126+ GPR_INFO, "HTTP:%d:%s:%s: %s%s", log_info_.stream_id, type,
127 log_info_.is_client ? "CLI" : "SVR", memento.md.DebugString().c_str(),
128 memento.parse_status == nullptr
129 ? ""
130@@ -951,11 +961,10 @@ class HPackParser::Parser {
131 state_.string_length)
132 : String::Parse(input_, state_.is_string_huff_compressed,
133 state_.string_length);
134- HpackParseResult& status = state_.frame_error;
135 absl::string_view key_string;
136 if (auto* s = absl::get_if<Slice>(&state_.key)) {
137 key_string = s->as_string_view();
138- if (status.ok()) {
139+ if (state_.field_error.ok()) {
140 auto r = ValidateKey(key_string);
141 if (r != ValidateMetadataResult::kOk) {
142 input_->SetErrorAndContinueParsing(
143@@ -965,7 +974,7 @@ class HPackParser::Parser {
144 } else {
145 const auto* memento = absl::get<const HPackTable::Memento*>(state_.key);
146 key_string = memento->md.key();
147- if (status.ok() && memento->parse_status != nullptr) {
148+ if (state_.field_error.ok() && memento->parse_status != nullptr) {
149 input_->SetErrorAndContinueParsing(*memento->parse_status);
150 }
151 }
152@@ -992,16 +1001,16 @@ class HPackParser::Parser {
153 key_string.size() + value.wire_size + hpack_constants::kEntryOverhead;
154 auto md = grpc_metadata_batch::Parse(
155 key_string, std::move(value_slice), state_.add_to_table, transport_size,
156- [key_string, &status, this](absl::string_view message, const Slice&) {
157- if (!status.ok()) return;
158+ [key_string, this](absl::string_view message, const Slice&) {
159+ if (!state_.field_error.ok()) return;
160 input_->SetErrorAndContinueParsing(
161 HpackParseResult::MetadataParseError(key_string));
162 gpr_log(GPR_ERROR, "Error parsing '%s' metadata: %s",
163 std::string(key_string).c_str(),
164 std::string(message).c_str());
165 });
166- HPackTable::Memento memento{std::move(md),
167- status.PersistentStreamErrorOrNullptr()};
168+ HPackTable::Memento memento{
169+ std::move(md), state_.field_error.PersistentStreamErrorOrNullptr()};
170 input_->UpdateFrontier();
171 state_.parse_state = ParseState::kTop;
172 if (state_.add_to_table) {
173@@ -1163,13 +1172,13 @@ grpc_error_handle HPackParser::Parse(
174 std::vector<uint8_t> buffer = std::move(unparsed_bytes_);
175 return ParseInput(
176 Input(nullptr, buffer.data(), buffer.data() + buffer.size(), bitsrc,
177- state_.frame_error),
178+ state_.frame_error, state_.field_error),
179 is_last, call_tracer);
180 }
181- return ParseInput(
182- Input(slice.refcount, GRPC_SLICE_START_PTR(slice),
183- GRPC_SLICE_END_PTR(slice), bitsrc, state_.frame_error),
184- is_last, call_tracer);
185+ return ParseInput(Input(slice.refcount, GRPC_SLICE_START_PTR(slice),
186+ GRPC_SLICE_END_PTR(slice), bitsrc, state_.frame_error,
187+ state_.field_error),
188+ is_last, call_tracer);
189 }
190
191 grpc_error_handle HPackParser::ParseInput(
192diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.h b/src/core/ext/transport/chttp2/transport/hpack_parser.h
193index 3745668..55842e4 100644
194--- a/src/core/ext/transport/chttp2/transport/hpack_parser.h
195+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.h
196@@ -236,6 +236,8 @@ class HPackParser {
197 HPackTable hpack_table;
198 // Error so far for this frame (set by class Input)
199 HpackParseResult frame_error;
200+ // Error so far for this field (set by class Input)
201+ HpackParseResult field_error;
202 // Length of frame so far.
203 uint32_t frame_length = 0;
204 // Length of the string being parsed
205diff --git a/test/core/transport/chttp2/hpack_parser_test.cc b/test/core/transport/chttp2/hpack_parser_test.cc
206index 3772d90..d5b9c6c 100644
207--- a/test/core/transport/chttp2/hpack_parser_test.cc
208+++ b/test/core/transport/chttp2/hpack_parser_test.cc
209@@ -440,19 +440,82 @@ INSTANTIATE_TEST_SUITE_P(
210 Test{"Base64LegalEncoding",
211 {},
212 {},
213- {// Binary metadata: created using:
214- // tools/codegen/core/gen_header_frame.py
215- // --compression inc --no_framing --output hexstr
216- // < test/core/transport/chttp2/bad-base64.headers
217- {"4009612e622e632d62696e1c6c75636b696c7920666f722075732c206974"
218- "27732074756573646179",
219- absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
220- "illegal base64 encoding"),
221- 0},
222- {"be",
223- absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
224- "illegal base64 encoding"),
225- 0}}},
226+ {
227+ // Binary metadata: created using:
228+ // tools/codegen/core/gen_header_frame.py
229+ // --compression inc --no_framing --output hexstr
230+ // < test/core/transport/chttp2/bad-base64.headers
231+ {"4009612e622e632d62696e1c6c75636b696c7920666f722075732c206974"
232+ "27732074756573646179",
233+ absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
234+ "illegal base64 encoding"),
235+ 0},
236+ {"be",
237+ absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
238+ "illegal base64 encoding"),
239+ kEndOfHeaders},
240+ {"82", ":method: GET\n", 0},
241+ }},
242+ Test{"Base64LegalEncodingWorksAfterFailure",
243+ {},
244+ {},
245+ {
246+ // Binary metadata: created using:
247+ // tools/codegen/core/gen_header_frame.py
248+ // --compression inc --no_framing --output hexstr
249+ // < test/core/transport/chttp2/bad-base64.headers
250+ {"4009612e622e632d62696e1c6c75636b696c7920666f722075732c206974"
251+ "27732074756573646179",
252+ absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
253+ "illegal base64 encoding"),
254+ 0},
255+ {"be",
256+ absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
257+ "illegal base64 encoding"),
258+ 0},
259+ {"400e636f6e74656e742d6c656e6774680135",
260+ absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
261+ "illegal base64 encoding"),
262+ kEndOfHeaders},
263+ {"be", "content-length: 5\n", 0},
264+ }},
265+ Test{"Base64LegalEncodingWorksAfterFailure2",
266+ {},
267+ {},
268+ {
269+ {// Generated with: tools/codegen/core/gen_header_frame.py
270+ // --compression inc --output hexstr --no_framing <
271+ // test/core/transport/chttp2/MiXeD-CaSe.headers
272+ "400a4d695865442d436153651073686f756c64206e6f74207061727365",
273+ absl::InternalError("Illegal header key: MiXeD-CaSe"), 0},
274+ // Binary metadata: created using:
275+ // tools/codegen/core/gen_header_frame.py
276+ // --compression inc --no_framing --output hexstr
277+ // < test/core/transport/chttp2/bad-base64.headers
278+ {"4009612e622e632d62696e1c6c75636b696c7920666f722075732c206974"
279+ "27732074756573646179",
280+ absl::InternalError("Illegal header key: MiXeD-CaSe"), 0},
281+ {"be", absl::InternalError("Illegal header key: MiXeD-CaSe"),
282+ 0},
283+ {"400e636f6e74656e742d6c656e6774680135",
284+ absl::InternalError("Illegal header key: MiXeD-CaSe"),
285+ kEndOfHeaders},
286+ {"be", "content-length: 5\n", 0},
287+ {"bf",
288+ absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
289+ "illegal base64 encoding"),
290+ 0},
291+ // Only the first error in each frame is reported, so we should
292+ // still see the same error here...
293+ {"c0",
294+ absl::InternalError("Error parsing 'a.b.c-bin' metadata: "
295+ "illegal base64 encoding"),
296+ kEndOfHeaders},
297+ // ... but if we look at the next frame we should see the
298+ // stored error
299+ {"c0", absl::InternalError("Illegal header key: MiXeD-CaSe"),
300+ kEndOfHeaders},
301+ }},
302 Test{"TeIsTrailers",
303 {},
304 {},
305diff --git a/test/core/transport/chttp2/hpack_sync_fuzzer.cc b/test/core/transport/chttp2/hpack_sync_fuzzer.cc
306index 47e4265..9afa41f 100644
307--- a/test/core/transport/chttp2/hpack_sync_fuzzer.cc
308+++ b/test/core/transport/chttp2/hpack_sync_fuzzer.cc
309@@ -85,6 +85,10 @@ void FuzzOneInput(const hpack_sync_fuzzer::Msg& msg) {
310 // Not an interesting case to fuzz
311 continue;
312 }
313+ if (msg.check_ab_preservation() &&
314+ header.literal_inc_idx().key() == "a") {
315+ continue;
316+ }
317 if (absl::EndsWith(header.literal_inc_idx().value(), "-bin")) {
318 std::ignore = encoder.EmitLitHdrWithBinaryStringKeyIncIdx(
319 Slice::FromCopiedString(header.literal_inc_idx().key()),
320@@ -96,6 +100,10 @@ void FuzzOneInput(const hpack_sync_fuzzer::Msg& msg) {
321 }
322 break;
323 case hpack_sync_fuzzer::Header::kLiteralNotIdx:
324+ if (msg.check_ab_preservation() &&
325+ header.literal_not_idx().key() == "a") {
326+ continue;
327+ }
328 if (absl::EndsWith(header.literal_not_idx().value(), "-bin")) {
329 encoder.EmitLitHdrWithBinaryStringKeyNotIdx(
330 Slice::FromCopiedString(header.literal_not_idx().key()),
331@@ -114,6 +122,10 @@ void FuzzOneInput(const hpack_sync_fuzzer::Msg& msg) {
332 break;
333 }
334 }
335+ if (msg.check_ab_preservation()) {
336+ std::ignore = encoder.EmitLitHdrWithNonBinaryStringKeyIncIdx(
337+ Slice::FromCopiedString("a"), Slice::FromCopiedString("b"));
338+ }
339
340 // STAGE 2: Decode the buffer (encode_output) into a list of headers
341 HPackParser parser;
342@@ -140,6 +152,21 @@ void FuzzOneInput(const hpack_sync_fuzzer::Msg& msg) {
343 }
344 }
345
346+ if (seen_errors.empty() && msg.check_ab_preservation()) {
347+ std::string backing;
348+ auto a_value = read_metadata.GetStringValue("a", &backing);
349+ if (!a_value.has_value()) {
350+ fprintf(stderr, "Expected 'a' header to be present: %s\n",
351+ read_metadata.DebugString().c_str());
352+ abort();
353+ }
354+ if (a_value != "b") {
355+ fprintf(stderr, "Expected 'a' header to be 'b', got '%s'\n",
356+ std::string(*a_value).c_str());
357+ abort();
358+ }
359+ }
360+
361 // STAGE 3: If we reached here we either had a stream error or no error
362 // parsing.
363 // Either way, the hpack tables should be of the same size between client and
364@@ -168,6 +195,41 @@ void FuzzOneInput(const hpack_sync_fuzzer::Msg& msg) {
365 }
366 abort();
367 }
368+
369+ if (msg.check_ab_preservation()) {
370+ SliceBuffer encode_output_2;
371+ hpack_encoder_detail::Encoder encoder_2(
372+ &compressor, msg.use_true_binary_metadata(), encode_output_2);
373+ encoder_2.EmitIndexed(62);
374+ GPR_ASSERT(encode_output_2.Count() == 1);
375+ grpc_metadata_batch read_metadata_2(arena.get());
376+ parser.BeginFrame(
377+ &read_metadata_2, 1024, 1024, HPackParser::Boundary::EndOfHeaders,
378+ HPackParser::Priority::None,
379+ HPackParser::LogInfo{3, HPackParser::LogInfo::kHeaders, false});
380+ auto err = parser.Parse(encode_output_2.c_slice_at(0), true,
381+ absl::BitGenRef(proto_bit_src),
382+ /*call_tracer=*/nullptr);
383+ if (!err.ok()) {
384+ fprintf(stderr, "Error parsing preservation encoded data: %s\n",
385+ err.ToString().c_str());
386+ abort();
387+ }
388+ std::string backing;
389+ auto a_value = read_metadata_2.GetStringValue("a", &backing);
390+ if (!a_value.has_value()) {
391+ fprintf(stderr,
392+ "Expected 'a' header to be present: %s\nfirst metadata: %s\n",
393+ read_metadata_2.DebugString().c_str(),
394+ read_metadata.DebugString().c_str());
395+ abort();
396+ }
397+ if (a_value != "b") {
398+ fprintf(stderr, "Expected 'a' header to be 'b', got '%s'\n",
399+ std::string(*a_value).c_str());
400+ abort();
401+ }
402+ }
403 }
404
405 } // namespace
406diff --git a/test/core/transport/chttp2/hpack_sync_fuzzer.proto b/test/core/transport/chttp2/hpack_sync_fuzzer.proto
407index 72792b6..2c075a6 100644
408--- a/test/core/transport/chttp2/hpack_sync_fuzzer.proto
409+++ b/test/core/transport/chttp2/hpack_sync_fuzzer.proto
410@@ -44,4 +44,7 @@ message Msg {
411 repeated Header headers = 2;
412 grpc.testing.FuzzConfigVars config_vars = 3;
413 repeated uint64 random_numbers = 4;
414+ // Ensure that a header "a: b" appended to headers with hpack incremental
415+ // indexing is correctly added to the hpack table.
416+ bool check_ab_preservation = 5;
417 }
418--
4192.44.1
420
diff --git a/meta-oe/recipes-devtools/grpc/grpc_1.60.1.bb b/meta-oe/recipes-devtools/grpc/grpc_1.60.1.bb
index 63c696a623..4557fcc908 100644
--- a/meta-oe/recipes-devtools/grpc/grpc_1.60.1.bb
+++ b/meta-oe/recipes-devtools/grpc/grpc_1.60.1.bb
@@ -24,6 +24,7 @@ SRCREV_grpc = "e5ae3b6b44bf3b64d24bfb4b4f82556239b986db"
24BRANCH = "v1.60.x" 24BRANCH = "v1.60.x"
25SRC_URI = "gitsm://github.com/grpc/grpc.git;protocol=https;name=grpc;branch=${BRANCH} \ 25SRC_URI = "gitsm://github.com/grpc/grpc.git;protocol=https;name=grpc;branch=${BRANCH} \
26 file://0001-cmake-Link-with-libatomic-on-rv32-rv64.patch \ 26 file://0001-cmake-Link-with-libatomic-on-rv32-rv64.patch \
27 file://CVE-2024-7246.patch \
27 " 28 "
28# Fixes build with older compilers 4.8 especially on ubuntu 14.04 29# Fixes build with older compilers 4.8 especially on ubuntu 14.04
29CXXFLAGS:append:class-native = " -Wl,--no-as-needed" 30CXXFLAGS:append:class-native = " -Wl,--no-as-needed"