diff --git a/0001-Backport-commit-be353d7.patch b/0001-Backport-commit-be353d7.patch new file mode 100644 index 0000000..f15dc8e --- /dev/null +++ b/0001-Backport-commit-be353d7.patch @@ -0,0 +1,122 @@ +From eb5017870dd6c96c1994a1b94150237e68edb179 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 12 Feb 2026 23:18:56 +0100 +Subject: [PATCH] Backport commit be353d7 + +Add limit of 20 continuation octets per OID arc to prevent a potential memory +exhaustion from excessive continuation bytes input. +--- + pyasn1/codec/ber/decoder.py | 11 ++++++ + tests/codec/ber/test_decoder.py | 66 +++++++++++++++++++++++++++++++++ + 2 files changed, 77 insertions(+) + +diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py +index 5ff485fbeb0cc7d761dd398134cf39a6258a109e..adfd7501a52421748b3143935a0ec95bdd1d42e6 100644 +--- a/pyasn1/codec/ber/decoder.py ++++ b/pyasn1/codec/ber/decoder.py +@@ -22,6 +22,10 @@ LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER) + + noValue = base.noValue + ++# Maximum number of continuation octets (high-bit set) allowed per OID arc. ++# 20 octets allows up to 140-bit integers, supporting UUID-based OIDs ++MAX_OID_ARC_CONTINUATION_OCTETS = 20 ++ + + class AbstractDecoder(object): + protoComponent = None +@@ -342,7 +346,14 @@ class ObjectIdentifierDecoder(AbstractSimpleDecoder): + # Construct subid from a number of octets + nextSubId = subId + subId = 0 ++ continuationOctetCount = 0 + while nextSubId >= 128: ++ continuationOctetCount += 1 ++ if continuationOctetCount > MAX_OID_ARC_CONTINUATION_OCTETS: ++ raise error.PyAsn1Error( ++ 'OID arc exceeds maximum continuation octets limit (%d) ' ++ 'at position %d' % (MAX_OID_ARC_CONTINUATION_OCTETS, index) ++ ) + subId = (subId << 7) + (nextSubId & 0x7F) + if index >= substrateLen: + raise error.SubstrateUnderrunError( +diff --git a/tests/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py +index e3b74dfd203923157cb6a26215e9d03c971a5b41..f9bf381b1da339611d974673420248e1a1014421 100644 +--- a/tests/codec/ber/test_decoder.py ++++ b/tests/codec/ber/test_decoder.py +@@ -407,6 +407,72 @@ class ObjectIdentifierDecoderTestCase(BaseTestCase): + ints2octs((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47)) + ) == ((2, 999, 18446744073709551535184467440737095), null) + ++ def testExcessiveContinuationOctets(self): ++ """Test that OID arcs with excessive continuation octets are rejected.""" ++ # Create a payload with 25 continuation octets (exceeds 20 limit) ++ # 0x81 bytes are continuation octets, 0x01 terminates ++ malicious_payload = bytes([0x06, 26]) + bytes([0x81] * 25) + bytes([0x01]) ++ try: ++ decoder.decode(malicious_payload) ++ except PyAsn1Error: ++ pass ++ else: ++ assert 0, 'Excessive continuation octets tolerated' ++ ++ def testMaxAllowedContinuationOctets(self): ++ """Test that OID arcs at the maximum continuation octets limit work.""" ++ # Create a payload with exactly 20 continuation octets (at limit) ++ # This should succeed ++ payload = bytes([0x06, 21]) + bytes([0x81] * 20) + bytes([0x01]) ++ try: ++ decoder.decode(payload) ++ except PyAsn1Error: ++ assert 0, 'Valid OID with 20 continuation octets rejected' ++ ++ def testOneOverContinuationLimit(self): ++ """Test boundary: 21 continuation octets (one over limit) is rejected.""" ++ payload = bytes([0x06, 22]) + bytes([0x81] * 21) + bytes([0x01]) ++ try: ++ decoder.decode(payload) ++ except PyAsn1Error: ++ pass ++ else: ++ assert 0, '21 continuation octets tolerated (should be rejected)' ++ ++ def testExcessiveContinuationInSecondArc(self): ++ """Test that limit applies to subsequent arcs, not just the first.""" ++ # First arc: valid simple byte (0x55 = 85, decodes to arc 2.5) ++ # Second arc: excessive continuation octets ++ payload = bytes([0x06, 27]) + bytes([0x55]) + bytes([0x81] * 25) + bytes([0x01]) ++ try: ++ decoder.decode(payload) ++ except PyAsn1Error: ++ pass ++ else: ++ assert 0, 'Excessive continuation in second arc tolerated' ++ ++ def testMultipleArcsAtLimit(self): ++ """Test multiple arcs each at the continuation limit work correctly.""" ++ # Two arcs, each with 20 continuation octets (both at limit) ++ arc1 = bytes([0x81] * 20) + bytes([0x01]) # 21 bytes ++ arc2 = bytes([0x81] * 20) + bytes([0x01]) # 21 bytes ++ payload = bytes([0x06, 42]) + arc1 + arc2 ++ try: ++ decoder.decode(payload) ++ except PyAsn1Error: ++ assert 0, 'Multiple valid arcs at limit rejected' ++ ++ def testExcessiveContinuationWithMaxBytes(self): ++ """Test with 0xFF continuation bytes (maximum value, not just 0x81).""" ++ # 0xFF bytes are also continuation octets (high bit set) ++ malicious_payload = bytes([0x06, 26]) + bytes([0xFF] * 25) + bytes([0x01]) ++ try: ++ decoder.decode(malicious_payload) ++ except PyAsn1Error: ++ pass ++ else: ++ assert 0, 'Excessive 0xFF continuation octets tolerated' ++ + + class RealDecoderTestCase(BaseTestCase): + def testChar(self): +-- +2.52.0 + diff --git a/python-pyasn1.spec b/python-pyasn1.spec index a487f46..8892a67 100644 --- a/python-pyasn1.spec +++ b/python-pyasn1.spec @@ -3,7 +3,7 @@ Name: python-pyasn1 Version: 0.4.8 -Release: 6%{?dist} +Release: 7%{?dist} Summary: ASN.1 tools for Python License: BSD Source0: https://github.com/etingof/pyasn1/archive/v%{version}.tar.gz @@ -11,6 +11,9 @@ Source1: https://github.com/etingof/pyasn1-modules/archive/v%{modules_ver URL: http://pyasn1.sourceforge.net/ BuildArch: noarch +Patch1: 0001-Backport-commit-be353d7.patch + + %description This is an implementation of ASN.1 types and codecs in the Python programming language. @@ -44,6 +47,7 @@ BuildRequires: python3-sphinx %prep %setup -n %{module}-%{version} -q -b1 +%autopatch -p1 %build @@ -85,6 +89,9 @@ PYTHONPATH=%{buildroot}%{python3_sitelib} %{__python3} setup.py test %doc docs/build/html/* %changelog +* Fri Feb 13 2026 Florence Blanc-Renaud - 0.4.8-7 +- Resolves: RHEL-148154 + * Tue Aug 10 2021 Mohan Boddu - 0.4.8-6 - Rebuilt for IMA sigs, glibc 2.34, aarch64 flags Related: rhbz#1991688 diff --git a/tests/tests.yml b/tests/tests.yml index 16696b5..b4db6f4 100644 --- a/tests/tests.yml +++ b/tests/tests.yml @@ -21,7 +21,7 @@ rm -rf pyasn1* && pytest-3 tests/ - unittests-pyasn1-modules: - dir: "source/pyasn1-modules-0.4.8" + dir: "source/pyasn1-modules-0.2.8" run: >- rm -rf pyasn1_modules* && pytest-3 tests/