summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChen Qi <Qi.Chen@windriver.com>2025-06-24 16:57:48 +0800
committerArmin Kuster <akuster808@gmail.com>2025-07-02 20:36:42 -0400
commit72f2dd38ca1bfae31190d3791adc2a9cac4da111 (patch)
tree425249408807538a8a55f3b02b81d3abf2e8f2a9
parent74f42273b4829a4adbb2356f5b441a6589d45d46 (diff)
downloadmeta-openembedded-72f2dd38ca1bfae31190d3791adc2a9cac4da111.tar.gz
protobuf: fix CVE-2025-4565
Backport patch with adjustments for 3.19.6 version to fix CVE-2025-4565. Signed-off-by: Chen Qi <Qi.Chen@windriver.com> Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r--meta-oe/recipes-devtools/protobuf/protobuf/CVE-2025-4565.patch376
-rw-r--r--meta-oe/recipes-devtools/protobuf/protobuf_3.19.6.bb1
2 files changed, 377 insertions, 0 deletions
diff --git a/meta-oe/recipes-devtools/protobuf/protobuf/CVE-2025-4565.patch b/meta-oe/recipes-devtools/protobuf/protobuf/CVE-2025-4565.patch
new file mode 100644
index 0000000000..acc9a87008
--- /dev/null
+++ b/meta-oe/recipes-devtools/protobuf/protobuf/CVE-2025-4565.patch
@@ -0,0 +1,376 @@
1From 0b48d64d7fcfd34514b6fa9b046d40457ed3e4b9 Mon Sep 17 00:00:00 2001
2From: shaod2 <shaod@google.com>
3Date: Wed, 21 May 2025 14:30:53 -0400
4Subject: [PATCH] Manually backport recursion limit enforcement to 25.x
5
6CVE: CVE-2025-4565
7
8Upstream-Status: Backport [d31100c9195819edb0a12f44705dfc2da111ea9b]
9Adjusted for the 3.19.6 version, resolving conflicts and removing
10unused testing codes.
11
12Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
13---
14 python/google/protobuf/internal/decoder.py | 110 ++++++++++++++----
15 .../protobuf/internal/python_message.py | 6 +-
16 .../protobuf/internal/self_recursive.proto | 17 +++
17 3 files changed, 106 insertions(+), 27 deletions(-)
18 create mode 100644 python/google/protobuf/internal/self_recursive.proto
19
20diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py
21index bc1b7b785..445c1d0d2 100644
22--- a/python/google/protobuf/internal/decoder.py
23+++ b/python/google/protobuf/internal/decoder.py
24@@ -195,7 +195,10 @@ def _SimpleDecoder(wire_type, decode_value):
25 clear_if_default=False):
26 if is_packed:
27 local_DecodeVarint = _DecodeVarint
28- def DecodePackedField(buffer, pos, end, message, field_dict):
29+ def DecodePackedField(
30+ buffer, pos, end, message, field_dict, current_depth=0
31+ ):
32+ del current_depth # unused
33 value = field_dict.get(key)
34 if value is None:
35 value = field_dict.setdefault(key, new_default(message))
36@@ -214,7 +217,10 @@ def _SimpleDecoder(wire_type, decode_value):
37 elif is_repeated:
38 tag_bytes = encoder.TagBytes(field_number, wire_type)
39 tag_len = len(tag_bytes)
40- def DecodeRepeatedField(buffer, pos, end, message, field_dict):
41+ def DecodeRepeatedField(
42+ buffer, pos, end, message, field_dict, current_depth=0
43+ ):
44+ del current_depth # unused
45 value = field_dict.get(key)
46 if value is None:
47 value = field_dict.setdefault(key, new_default(message))
48@@ -231,7 +237,8 @@ def _SimpleDecoder(wire_type, decode_value):
49 return new_pos
50 return DecodeRepeatedField
51 else:
52- def DecodeField(buffer, pos, end, message, field_dict):
53+ def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
54+ del current_depth # unused
55 (new_value, pos) = decode_value(buffer, pos)
56 if pos > end:
57 raise _DecodeError('Truncated message.')
58@@ -375,7 +382,9 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default,
59 enum_type = key.enum_type
60 if is_packed:
61 local_DecodeVarint = _DecodeVarint
62- def DecodePackedField(buffer, pos, end, message, field_dict):
63+ def DecodePackedField(
64+ buffer, pos, end, message, field_dict, current_depth=0
65+ ):
66 """Decode serialized packed enum to its value and a new position.
67
68 Args:
69@@ -388,6 +397,7 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default,
70 Returns:
71 int, new position in serialized data.
72 """
73+ del current_depth # unused
74 value = field_dict.get(key)
75 if value is None:
76 value = field_dict.setdefault(key, new_default(message))
77@@ -428,7 +438,9 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default,
78 elif is_repeated:
79 tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_VARINT)
80 tag_len = len(tag_bytes)
81- def DecodeRepeatedField(buffer, pos, end, message, field_dict):
82+ def DecodeRepeatedField(
83+ buffer, pos, end, message, field_dict, current_depth=0
84+ ):
85 """Decode serialized repeated enum to its value and a new position.
86
87 Args:
88@@ -441,6 +453,7 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default,
89 Returns:
90 int, new position in serialized data.
91 """
92+ del current_depth # unused
93 value = field_dict.get(key)
94 if value is None:
95 value = field_dict.setdefault(key, new_default(message))
96@@ -469,7 +482,7 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default,
97 return new_pos
98 return DecodeRepeatedField
99 else:
100- def DecodeField(buffer, pos, end, message, field_dict):
101+ def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
102 """Decode serialized repeated enum to its value and a new position.
103
104 Args:
105@@ -482,6 +495,7 @@ def EnumDecoder(field_number, is_repeated, is_packed, key, new_default,
106 Returns:
107 int, new position in serialized data.
108 """
109+ del current_depth # unused
110 value_start_pos = pos
111 (enum_value, pos) = _DecodeSignedVarint32(buffer, pos)
112 if pos > end:
113@@ -563,7 +577,10 @@ def StringDecoder(field_number, is_repeated, is_packed, key, new_default,
114 tag_bytes = encoder.TagBytes(field_number,
115 wire_format.WIRETYPE_LENGTH_DELIMITED)
116 tag_len = len(tag_bytes)
117- def DecodeRepeatedField(buffer, pos, end, message, field_dict):
118+ def DecodeRepeatedField(
119+ buffer, pos, end, message, field_dict, current_depth=0
120+ ):
121+ del current_depth # unused
122 value = field_dict.get(key)
123 if value is None:
124 value = field_dict.setdefault(key, new_default(message))
125@@ -580,7 +597,8 @@ def StringDecoder(field_number, is_repeated, is_packed, key, new_default,
126 return new_pos
127 return DecodeRepeatedField
128 else:
129- def DecodeField(buffer, pos, end, message, field_dict):
130+ def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
131+ del current_depth # unused
132 (size, pos) = local_DecodeVarint(buffer, pos)
133 new_pos = pos + size
134 if new_pos > end:
135@@ -604,7 +622,10 @@ def BytesDecoder(field_number, is_repeated, is_packed, key, new_default,
136 tag_bytes = encoder.TagBytes(field_number,
137 wire_format.WIRETYPE_LENGTH_DELIMITED)
138 tag_len = len(tag_bytes)
139- def DecodeRepeatedField(buffer, pos, end, message, field_dict):
140+ def DecodeRepeatedField(
141+ buffer, pos, end, message, field_dict, current_depth=0
142+ ):
143+ del current_depth # unused
144 value = field_dict.get(key)
145 if value is None:
146 value = field_dict.setdefault(key, new_default(message))
147@@ -621,7 +642,8 @@ def BytesDecoder(field_number, is_repeated, is_packed, key, new_default,
148 return new_pos
149 return DecodeRepeatedField
150 else:
151- def DecodeField(buffer, pos, end, message, field_dict):
152+ def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
153+ del current_depth # unused
154 (size, pos) = local_DecodeVarint(buffer, pos)
155 new_pos = pos + size
156 if new_pos > end:
157@@ -646,7 +668,9 @@ def GroupDecoder(field_number, is_repeated, is_packed, key, new_default):
158 tag_bytes = encoder.TagBytes(field_number,
159 wire_format.WIRETYPE_START_GROUP)
160 tag_len = len(tag_bytes)
161- def DecodeRepeatedField(buffer, pos, end, message, field_dict):
162+ def DecodeRepeatedField(
163+ buffer, pos, end, message, field_dict, current_depth=0
164+ ):
165 value = field_dict.get(key)
166 if value is None:
167 value = field_dict.setdefault(key, new_default(message))
168@@ -655,7 +679,13 @@ def GroupDecoder(field_number, is_repeated, is_packed, key, new_default):
169 if value is None:
170 value = field_dict.setdefault(key, new_default(message))
171 # Read sub-message.
172- pos = value.add()._InternalParse(buffer, pos, end)
173+ current_depth += 1
174+ if current_depth > _recursion_limit:
175+ raise _DecodeError(
176+ 'Error parsing message: too many levels of nesting.'
177+ )
178+ pos = value.add()._InternalParse(buffer, pos, end, current_depth)
179+ current_depth -= 1
180 # Read end tag.
181 new_pos = pos+end_tag_len
182 if buffer[pos:new_pos] != end_tag_bytes or new_pos > end:
183@@ -667,12 +697,16 @@ def GroupDecoder(field_number, is_repeated, is_packed, key, new_default):
184 return new_pos
185 return DecodeRepeatedField
186 else:
187- def DecodeField(buffer, pos, end, message, field_dict):
188+ def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
189 value = field_dict.get(key)
190 if value is None:
191 value = field_dict.setdefault(key, new_default(message))
192 # Read sub-message.
193- pos = value._InternalParse(buffer, pos, end)
194+ current_depth += 1
195+ if current_depth > _recursion_limit:
196+ raise _DecodeError('Error parsing message: too many levels of nesting.')
197+ pos = value._InternalParse(buffer, pos, end, current_depth)
198+ current_depth -= 1
199 # Read end tag.
200 new_pos = pos+end_tag_len
201 if buffer[pos:new_pos] != end_tag_bytes or new_pos > end:
202@@ -691,7 +725,9 @@ def MessageDecoder(field_number, is_repeated, is_packed, key, new_default):
203 tag_bytes = encoder.TagBytes(field_number,
204 wire_format.WIRETYPE_LENGTH_DELIMITED)
205 tag_len = len(tag_bytes)
206- def DecodeRepeatedField(buffer, pos, end, message, field_dict):
207+ def DecodeRepeatedField(
208+ buffer, pos, end, message, field_dict, current_depth=0
209+ ):
210 value = field_dict.get(key)
211 if value is None:
212 value = field_dict.setdefault(key, new_default(message))
213@@ -702,18 +738,27 @@ def MessageDecoder(field_number, is_repeated, is_packed, key, new_default):
214 if new_pos > end:
215 raise _DecodeError('Truncated message.')
216 # Read sub-message.
217- if value.add()._InternalParse(buffer, pos, new_pos) != new_pos:
218+ current_depth += 1
219+ if current_depth > _recursion_limit:
220+ raise _DecodeError(
221+ 'Error parsing message: too many levels of nesting.'
222+ )
223+ if (
224+ value.add()._InternalParse(buffer, pos, new_pos, current_depth)
225+ != new_pos
226+ ):
227 # The only reason _InternalParse would return early is if it
228 # encountered an end-group tag.
229 raise _DecodeError('Unexpected end-group tag.')
230 # Predict that the next tag is another copy of the same repeated field.
231+ current_depth -= 1
232 pos = new_pos + tag_len
233 if buffer[new_pos:pos] != tag_bytes or new_pos == end:
234 # Prediction failed. Return.
235 return new_pos
236 return DecodeRepeatedField
237 else:
238- def DecodeField(buffer, pos, end, message, field_dict):
239+ def DecodeField(buffer, pos, end, message, field_dict, current_depth=0):
240 value = field_dict.get(key)
241 if value is None:
242 value = field_dict.setdefault(key, new_default(message))
243@@ -722,11 +767,14 @@ def MessageDecoder(field_number, is_repeated, is_packed, key, new_default):
244 new_pos = pos + size
245 if new_pos > end:
246 raise _DecodeError('Truncated message.')
247- # Read sub-message.
248- if value._InternalParse(buffer, pos, new_pos) != new_pos:
249+ current_depth += 1
250+ if current_depth > _recursion_limit:
251+ raise _DecodeError('Error parsing message: too many levels of nesting.')
252+ if value._InternalParse(buffer, pos, new_pos, current_depth) != new_pos:
253 # The only reason _InternalParse would return early is if it encountered
254 # an end-group tag.
255 raise _DecodeError('Unexpected end-group tag.')
256+ current_depth -= 1
257 return new_pos
258 return DecodeField
259
260@@ -844,7 +892,8 @@ def MapDecoder(field_descriptor, new_default, is_message_map):
261 # Can't read _concrete_class yet; might not be initialized.
262 message_type = field_descriptor.message_type
263
264- def DecodeMap(buffer, pos, end, message, field_dict):
265+ def DecodeMap(buffer, pos, end, message, field_dict, current_depth=0):
266+ del current_depth # unused
267 submsg = message_type._concrete_class()
268 value = field_dict.get(key)
269 if value is None:
270@@ -926,8 +975,16 @@ def _SkipGroup(buffer, pos, end):
271 return pos
272 pos = new_pos
273
274+DEFAULT_RECURSION_LIMIT = 100
275+_recursion_limit = DEFAULT_RECURSION_LIMIT
276+
277+
278+def SetRecursionLimit(new_limit):
279+ global _recursion_limit
280+ _recursion_limit = new_limit
281+
282
283-def _DecodeUnknownFieldSet(buffer, pos, end_pos=None):
284+def _DecodeUnknownFieldSet(buffer, pos, end_pos=None, current_depth=0):
285 """Decode UnknownFieldSet. Returns the UnknownFieldSet and new position."""
286
287 unknown_field_set = containers.UnknownFieldSet()
288@@ -937,14 +994,14 @@ def _DecodeUnknownFieldSet(buffer, pos, end_pos=None):
289 field_number, wire_type = wire_format.UnpackTag(tag)
290 if wire_type == wire_format.WIRETYPE_END_GROUP:
291 break
292- (data, pos) = _DecodeUnknownField(buffer, pos, wire_type)
293+ (data, pos) = _DecodeUnknownField(buffer, pos, wire_type, current_depth)
294 # pylint: disable=protected-access
295 unknown_field_set._add(field_number, wire_type, data)
296
297 return (unknown_field_set, pos)
298
299
300-def _DecodeUnknownField(buffer, pos, wire_type):
301+def _DecodeUnknownField(buffer, pos, wire_type, current_depth=0):
302 """Decode a unknown field. Returns the UnknownField and new position."""
303
304 if wire_type == wire_format.WIRETYPE_VARINT:
305@@ -958,7 +1015,12 @@ def _DecodeUnknownField(buffer, pos, wire_type):
306 data = buffer[pos:pos+size].tobytes()
307 pos += size
308 elif wire_type == wire_format.WIRETYPE_START_GROUP:
309- (data, pos) = _DecodeUnknownFieldSet(buffer, pos)
310+ print("MMP " + str(current_depth))
311+ current_depth += 1
312+ if current_depth >= _recursion_limit:
313+ raise _DecodeError('Error parsing message: too many levels of nesting.')
314+ (data, pos) = _DecodeUnknownFieldSet(buffer, pos, None, current_depth)
315+ current_depth -= 1
316 elif wire_type == wire_format.WIRETYPE_END_GROUP:
317 return (0, -1)
318 else:
319diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py
320index 2921d5cb6..c7fec8c1c 100644
321--- a/python/google/protobuf/internal/python_message.py
322+++ b/python/google/protobuf/internal/python_message.py
323@@ -1141,7 +1141,7 @@ def _AddMergeFromStringMethod(message_descriptor, cls):
324 local_SkipField = decoder.SkipField
325 decoders_by_tag = cls._decoders_by_tag
326
327- def InternalParse(self, buffer, pos, end):
328+ def InternalParse(self, buffer, pos, end, current_depth=0):
329 """Create a message from serialized bytes.
330
331 Args:
332@@ -1179,7 +1179,7 @@ def _AddMergeFromStringMethod(message_descriptor, cls):
333 # TODO(jieluo): remove old_pos.
334 old_pos = new_pos
335 (data, new_pos) = decoder._DecodeUnknownField(
336- buffer, new_pos, wire_type) # pylint: disable=protected-access
337+ buffer, new_pos, wire_type, current_depth) # pylint: disable=protected-access
338 if new_pos == -1:
339 return pos
340 # pylint: disable=protected-access
341@@ -1192,7 +1192,7 @@ def _AddMergeFromStringMethod(message_descriptor, cls):
342 (tag_bytes, buffer[old_pos:new_pos].tobytes()))
343 pos = new_pos
344 else:
345- pos = field_decoder(buffer, new_pos, end, self, field_dict)
346+ pos = field_decoder(buffer, new_pos, end, self, field_dict, current_depth)
347 if field_desc:
348 self._UpdateOneofState(field_desc)
349 return pos
350diff --git a/python/google/protobuf/internal/self_recursive.proto b/python/google/protobuf/internal/self_recursive.proto
351new file mode 100644
352index 000000000..2a7aacb0b
353--- /dev/null
354+++ b/python/google/protobuf/internal/self_recursive.proto
355@@ -0,0 +1,17 @@
356+// Protocol Buffers - Google's data interchange format
357+// Copyright 2024 Google Inc. All rights reserved.
358+//
359+// Use of this source code is governed by a BSD-style
360+// license that can be found in the LICENSE file or at
361+// https://developers.google.com/open-source/licenses/bsd
362+
363+syntax = "proto2";
364+
365+package google.protobuf.python.internal;
366+
367+message SelfRecursive {
368+ optional group RecursiveGroup = 1 {
369+ optional RecursiveGroup sub_group = 2;
370+ optional int32 i = 3;
371+ };
372+}
373\ No newline at end of file
374--
3752.34.1
376
diff --git a/meta-oe/recipes-devtools/protobuf/protobuf_3.19.6.bb b/meta-oe/recipes-devtools/protobuf/protobuf_3.19.6.bb
index 9bcd208acb..95a76514a5 100644
--- a/meta-oe/recipes-devtools/protobuf/protobuf_3.19.6.bb
+++ b/meta-oe/recipes-devtools/protobuf/protobuf_3.19.6.bb
@@ -20,6 +20,7 @@ SRC_URI = "git://github.com/protocolbuffers/protobuf.git;branch=3.19.x;protocol=
20 file://0001-Fix-linking-error-with-ld-gold.patch \ 20 file://0001-Fix-linking-error-with-ld-gold.patch \
21 file://0001-Lower-init-prio-for-extension-attributes.patch \ 21 file://0001-Lower-init-prio-for-extension-attributes.patch \
22 file://0001-Add-recursion-check-when-parsing-unknown-fields-in-J.patch \ 22 file://0001-Add-recursion-check-when-parsing-unknown-fields-in-J.patch \
23 file://CVE-2025-4565.patch \
23 " 24 "
24SRC_URI:append:mips:toolchain-clang = " file://0001-Fix-build-on-mips-clang.patch " 25SRC_URI:append:mips:toolchain-clang = " file://0001-Fix-build-on-mips-clang.patch "
25SRC_URI:append:mipsel:toolchain-clang = " file://0001-Fix-build-on-mips-clang.patch " 26SRC_URI:append:mipsel:toolchain-clang = " file://0001-Fix-build-on-mips-clang.patch "