Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions SPECS/mysql/CVE-2026-0994.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
From 5ebddcb1bcbe51d1fe323baa145e85f4f23128cf Mon Sep 17 00:00:00 2001
From: zhangskz <sandyzhang@google.com>
Date: Thu, 29 Jan 2026 16:32:56 -0500
Subject: [PATCH] Fix Any recursion depth bypass in Python
json_format.ParseDict (#25239) (#25587)

This fixes a security vulnerability where nested google.protobuf.Any messages could bypass the max_recursion_depth limit, potentially leading to denial of service via stack overflow.

The root cause was that _ConvertAnyMessage() was calling itself recursively via methodcaller() for nested well-known types, bypassing the recursion depth tracking in ConvertMessage().

The fix routes well-known type parsing through ConvertMessage() to ensure proper recursion depth accounting for all message types including nested Any.

Fixes #25070

Closes #25239

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/25239 from aviralgarg05:fix-any-recursion-depth-bypass 3cbbcbea142593d3afd2ceba2db14b05660f62f4
PiperOrigin-RevId: 862740421

Co-authored-by: Aviral Garg <gargaviral99@gmail.com>

Upstream Patch Reference: https://github.com/protocolbuffers/protobuf/commit/5ebddcb1bcbe51d1fe323baa145e85f4f23128cf.patch
---
.../protobuf/internal/json_format_test.py | 102 ++++++++++++++++++
.../python/google/protobuf/json_format.py | 12 ++-
2 files changed, 111 insertions(+), 3 deletions(-)

diff --git a/extra/protobuf/protobuf-24.4/python/google/protobuf/internal/json_format_test.py b/extra/protobuf/protobuf-24.4/python/google/protobuf/internal/json_format_test.py
index b4f516d8..d2967801 100644
--- a/extra/protobuf/protobuf-24.4/python/google/protobuf/internal/json_format_test.py
+++ b/extra/protobuf/protobuf-24.4/python/google/protobuf/internal/json_format_test.py
@@ -1297,6 +1297,108 @@ class JsonFormatTest(JsonFormatBase):
# The following one can pass
json_format.Parse('{"payload": {}, "child": {"child":{}}}',
message, max_recursion_depth=3)
+
+ def testAnyRecursionDepthEnforcement(self):
+ """Test that nested Any messages respect max_recursion_depth limit."""
+ # Test that deeply nested Any messages raise ParseError instead of
+ # bypassing the recursion limit. This prevents DoS via nested Any.
+ message = any_pb2.Any()
+
+ # Create nested Any structure that should exceed depth limit
+ # With max_recursion_depth=5, we can nest 4 Any messages
+ # (depth 1 = outer Any, depth 2-4 = nested Anys, depth 5 = final value)
+ nested_any = {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {},
+ },
+ },
+ },
+ },
+ }
+
+ # Should raise ParseError due to exceeding max depth, not RecursionError
+ self.assertRaisesRegex(
+ json_format.ParseError,
+ 'Message too deep. Max recursion depth is 5',
+ json_format.ParseDict,
+ nested_any,
+ message,
+ max_recursion_depth=5,
+ )
+
+ # Verify that Any messages within the limit can be parsed successfully
+ # With max_recursion_depth=5, we can nest up to 4 Any messages
+ shallow_any = {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {},
+ },
+ },
+ },
+ }
+ json_format.ParseDict(shallow_any, message, max_recursion_depth=5)
+
+ def testAnyRecursionDepthBoundary(self):
+ """Test recursion depth boundary behavior (exclusive upper limit)."""
+ message = any_pb2.Any()
+
+ # Create nested Any at depth exactly 4 (should succeed with max_recursion_depth=5)
+ depth_4_any = {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {},
+ },
+ },
+ },
+ }
+ # This should succeed: depth 4 < max_recursion_depth 5
+ json_format.ParseDict(depth_4_any, message, max_recursion_depth=5)
+
+ # Create nested Any at depth exactly 5 (should fail with max_recursion_depth=5)
+ depth_5_any = {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {
+ '@type': 'type.googleapis.com/google.protobuf.Any',
+ 'value': {},
+ },
+ },
+ },
+ },
+ }
+ # This should fail: depth 5 == max_recursion_depth 5 (exclusive limit)
+ self.assertRaisesRegex(
+ json_format.ParseError,
+ 'Message too deep. Max recursion depth is 5',
+ json_format.ParseDict,
+ depth_5_any,
+ message,
+ max_recursion_depth=5,
+ )
+

if __name__ == '__main__':
unittest.main()
diff --git a/extra/protobuf/protobuf-24.4/python/google/protobuf/json_format.py b/extra/protobuf/protobuf-24.4/python/google/protobuf/json_format.py
index a04e8aef..2fe8da64 100644
--- a/extra/protobuf/protobuf-24.4/python/google/protobuf/json_format.py
+++ b/extra/protobuf/protobuf-24.4/python/google/protobuf/json_format.py
@@ -496,6 +496,10 @@ class _Parser(object):
Raises:
ParseError: In case of convert problems.
"""
+ # Increment recursion depth at message entry. The max_recursion_depth limit
+ # is exclusive: a depth value equal to max_recursion_depth will trigger an
+ # error. For example, with max_recursion_depth=5, nesting up to depth 4 is
+ # allowed, but attempting depth 5 raises ParseError.
self.recursion_depth += 1
if self.recursion_depth > self.max_recursion_depth:
raise ParseError('Message too deep. Max recursion depth is {0}'.format(
@@ -669,9 +673,11 @@ class _Parser(object):
self._ConvertWrapperMessage(value['value'], sub_message,
'{0}.value'.format(path))
elif full_name in _WKTJSONMETHODS:
- methodcaller(_WKTJSONMETHODS[full_name][1], value['value'], sub_message,
- '{0}.value'.format(path))(
- self)
+ # For well-known types (including nested Any), use ConvertMessage
+ # to ensure recursion depth is properly tracked
+ self.ConvertMessage(
+ value['value'], sub_message, '{0}.value'.format(path)
+ )
else:
del value['@type']
self._ConvertFieldValuePair(value, sub_message, path)
--
2.45.4

6 changes: 5 additions & 1 deletion SPECS/mysql/mysql.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Summary: MySQL.
Name: mysql
Version: 8.0.45
Release: 1%{?dist}
Release: 2%{?dist}
License: GPLv2 with exceptions AND LGPLv2 AND BSD
Vendor: Microsoft Corporation
Distribution: Mariner
Expand All @@ -15,6 +15,7 @@ Patch1: CVE-2024-2410.patch
# ciphers unavailable.
Patch2: fix-tests-for-unsupported-chacha-ciphers.patch
Patch3: CVE-2025-62813.patch
Patch4: CVE-2026-0994.patch
BuildRequires: cmake
BuildRequires: libtirpc-devel
BuildRequires: openssl-devel
Expand Down Expand Up @@ -115,6 +116,9 @@ fi
%{_libdir}/pkgconfig/mysqlclient.pc

%changelog
* Mon Feb 09 2026 Jyoti Kanase <v-jykanase@microsoft.com> - 8.0.45-2
- Patch for CVE-2026-0994

* Wed Jan 21 2026 Kanishk Bansal <kanbansal@microsoft.com> - 8.0.45-1
- Upgrade to 8.0.45 for CVE-2026-21948, CVE-2026-21968,
CVE-2026-21941, CVE-2026-21964, CVE-2026-21936, CVE-2026-21937
Expand Down
Loading