From 3014d7933d94af5670032772e4753d3aa6b83176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=BF=8A=E9=BE=99?= Date: Mon, 7 Jul 2025 14:54:07 +0800 Subject: [PATCH] =?UTF-8?q?Add=20recursion=20depth=20limits=20to=20pure=20?= =?UTF-8?q?python=20Signed-off-by:=20=E7=8E=8B=E4=BF=8A=E9=BE=99=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/google/protobuf/internal/decoder.py | 32 ++++++++++ .../google/protobuf/internal/decoder_test.py | 14 +++++ .../google/protobuf/internal/message_test.py | 59 +++++++++++++++++-- .../protobuf/internal/self_recursive.proto | 2 + 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py index dcde1d9..2ffaede 100644 --- a/python/google/protobuf/internal/decoder.py +++ b/python/google/protobuf/internal/decoder.py @@ -630,7 +630,13 @@ def GroupDecoder(field_number, is_repeated, is_packed, key, new_default): if value is None: value = field_dict.setdefault(key, new_default(message)) # Read sub-message. + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError( + 'Error parsing message: too many levels of nesting.' + ) pos = value.add()._InternalParse(buffer, pos, end) + current_depth -= 1 # Read end tag. new_pos = pos+end_tag_len if buffer[pos:new_pos] != end_tag_bytes or new_pos > end: @@ -647,7 +653,11 @@ def GroupDecoder(field_number, is_repeated, is_packed, key, new_default): if value is None: value = field_dict.setdefault(key, new_default(message)) # Read sub-message. + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError('Error parsing message: too many levels of nesting.') pos = value._InternalParse(buffer, pos, end) + current_depth -= 1 # Read end tag. new_pos = pos+end_tag_len if buffer[pos:new_pos] != end_tag_bytes or new_pos > end: @@ -677,10 +687,16 @@ def MessageDecoder(field_number, is_repeated, is_packed, key, new_default): if new_pos > end: raise _DecodeError('Truncated message.') # Read sub-message. + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError( + 'Error parsing message: too many levels of nesting.' + ) if value.add()._InternalParse(buffer, pos, new_pos) != new_pos: # The only reason _InternalParse would return early is if it # encountered an end-group tag. raise _DecodeError('Unexpected end-group tag.') + current_depth -= 1 # Predict that the next tag is another copy of the same repeated field. pos = new_pos + tag_len if buffer[new_pos:pos] != tag_bytes or new_pos == end: @@ -698,10 +714,14 @@ def MessageDecoder(field_number, is_repeated, is_packed, key, new_default): if new_pos > end: raise _DecodeError('Truncated message.') # Read sub-message. + current_depth += 1 + if current_depth > _recursion_limit: + raise _DecodeError('Error parsing message: too many levels of nesting.') if value._InternalParse(buffer, pos, new_pos) != new_pos: # The only reason _InternalParse would return early is if it encountered # an end-group tag. raise _DecodeError('Unexpected end-group tag.') + current_depth -= 1 return new_pos return DecodeField @@ -965,7 +985,11 @@ def _DecodeUnknownField(buffer, pos, wire_type): data = buffer[pos:pos+size].tobytes() pos += size elif wire_type == wire_format.WIRETYPE_START_GROUP: + current_depth += 1 + if current_depth >= _recursion_limit: + raise _DecodeError('Error parsing message: too many levels of nesting.') (data, pos) = _DecodeUnknownFieldSet(buffer, pos) + current_depth -= 1 elif wire_type == wire_format.WIRETYPE_END_GROUP: return (0, -1) else: @@ -995,6 +1019,14 @@ def _DecodeFixed32(buffer, pos): new_pos = pos + 4 return (struct.unpack('