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