diff --git a/python-pyasn1-0.3.7-CVE-2026-59886.patch b/python-pyasn1-0.3.7-CVE-2026-59886.patch new file mode 100644 index 0000000..a41d480 --- /dev/null +++ b/python-pyasn1-0.3.7-CVE-2026-59886.patch @@ -0,0 +1,247 @@ +From e8f259647c04cf9a41130c8387cef2690d8ec19e Mon Sep 17 00:00:00 2001 +From: Simon Pichugin +Date: Wed, 8 Jul 2026 17:32:09 -0700 +Subject: [PATCH] Merge commit from fork + +--- + pyasn1/type/univ.py | 21 ++++++++++--- + tests/codec/ber/test_decoder.py | 54 +++++++++++++++++++++++++++------ + tests/codec/cer/test_decoder.py | 11 +++++++ + tests/codec/der/test_decoder.py | 20 ++++++++++++ + tests/type/test_univ.py | 40 ++++++++++++++++++++++++ + 5 files changed, 131 insertions(+), 15 deletions(-) + +diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py +index a90c648..5d6c23a 100644 +--- a/pyasn1/type/univ.py ++++ b/pyasn1/type/univ.py +@@ -1444,7 +1444,7 @@ class Real(base.AbstractSimpleAsn1Item): + def __normalizeBase10(value): + m, b, e = value + while m and m % 10 == 0: +- m /= 10 ++ m //= 10 + e += 1 + return m, b, e + +@@ -1595,10 +1595,21 @@ class Real(base.AbstractSimpleAsn1Item): + def __float__(self): + if self._value in self._inf: + return self._value +- else: +- return float( +- self._value[0] * pow(self._value[1], self._value[2]) +- ) ++ ++ mantissa, base, exponent = self._value ++ ++ if not mantissa: ++ return 0.0 ++ ++ if base == 2: ++ return math.ldexp(float(mantissa), exponent) ++ ++ # base is 10 (prettyIn() rejects everything else); refuse to ++ # materialize astronomically large integers via pow() ++ if exponent > sys.float_info.max_10_exp: ++ raise OverflowError('Real value too large to convert to float') ++ ++ return float(mantissa * pow(base, exponent)) + + def __abs__(self): + return self.clone(abs(float(self))) +diff --git a/tests/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py +index ba7dfec..9c98864 100644 +--- a/tests/codec/ber/test_decoder.py ++++ b/tests/codec/ber/test_decoder.py +@@ -13,7 +13,7 @@ except ImportError: + from tests.base import BaseTestCase + + from pyasn1.type import tag, namedtype, univ, char +-from pyasn1.codec.ber import decoder, eoo ++from pyasn1.codec.ber import decoder, encoder, eoo + from pyasn1.compat.octets import ints2octs, str2octs, null + from pyasn1.error import PyAsn1Error + +@@ -501,17 +501,51 @@ class RealDecoderTestCase(BaseTestCase): + ints2octs((9, 4, 161, 255, 1, 3)) + ) == (univ.Real((3, 2, -1020)), null) + +-# TODO: this requires Real type comparison fix ++ def testBin6(self): # large exponent, base = 16 ++ value, rest = decoder.decode( ++ ints2octs((9, 5, 162, 0, 255, 255, 1)) ++ ) ++ ++ assert tuple(value) == (1, 2, 262140) ++ assert rest == null ++ ++ def testBin7(self): # large exponent in 4-octet form, base = 16 ++ value, rest = decoder.decode( ++ ints2octs((9, 7, 227, 4, 1, 35, 69, 103, 1)) ++ ) + +-# def testBin6(self): +-# assert decoder.decode( +-# ints2octs((9, 5, 162, 0, 255, 255, 1)) +-# ) == (univ.Real((1, 2, 262140)), null) ++ assert tuple(value) == (-1, 2, 76354972) ++ assert rest == null ++ ++ def testLargeBinaryRoundTrip(self): ++ substrate = encoder.encode(univ.Real((-1, 2, 76354972))) ++ value, rest = decoder.decode(substrate) ++ ++ assert tuple(value) == (-1, 2, 76354972) ++ assert rest == null ++ ++ def testLongFormBinaryRealExponentLength(self): ++ value, rest = decoder.decode( ++ ints2octs((9, 6, 0x83, 3, 0x0f, 0x42, 0x40, 1)) ++ ) + +-# def testBin7(self): +-# assert decoder.decode( +-# ints2octs((9, 7, 227, 4, 1, 35, 69, 103, 1)) +-# ) == (univ.Real((-1, 2, 76354972)), null) ++ assert tuple(value) == (1, 2, 1000000) ++ assert rest == null ++ ++ def testLargeBinaryPrettyPrintOverflow(self): ++ value, rest = decoder.decode( ++ b'\t\t\xeb\x060662.666\xd0B\x00\x00\x00\x00\x00\x00\x00' ++ ) ++ ++ assert value.prettyPrint() == '' ++ assert rest == b'6\xd0B\x00\x00\x00\x00\x00\x00\x00' ++ ++ try: ++ float(value) ++ except OverflowError: ++ pass ++ else: ++ assert 0, '__float__() tolerated overflow' + + def testPlusInf(self): + assert decoder.decode( +diff --git a/tests/codec/cer/test_decoder.py b/tests/codec/cer/test_decoder.py +index 828c17f..c4a91db 100644 +--- a/tests/codec/cer/test_decoder.py ++++ b/tests/codec/cer/test_decoder.py +@@ -14,6 +14,7 @@ except ImportError: + from tests.base import BaseTestCase + + from pyasn1.codec.cer import decoder ++from pyasn1.codec.cer import encoder + from pyasn1.compat.octets import ints2octs, str2octs, null + from pyasn1.error import PyAsn1Error + +@@ -65,6 +66,16 @@ class OctetStringDecoderTestCase(BaseTestCase): + # TODO: test failures on short chunked and long unchunked substrate samples + + ++class RealDecoderTestCase(BaseTestCase): ++ def testLargeBinaryRoundTrip(self): ++ substrate = encoder.encode(univ.Real((-1, 2, 76354972))) ++ value, rest = decoder.decode(substrate) ++ ++ assert tuple(value) == (-1, 2, 76354972) ++ assert rest == null ++ ++ ++ + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + + if __name__ == '__main__': +diff --git a/tests/codec/der/test_decoder.py b/tests/codec/der/test_decoder.py +index 6f292fd..08101a4 100644 +--- a/tests/codec/der/test_decoder.py ++++ b/tests/codec/der/test_decoder.py +@@ -14,6 +14,7 @@ except ImportError: + from tests.base import BaseTestCase + + from pyasn1.codec.der import decoder ++from pyasn1.codec.der import encoder + from pyasn1.compat.octets import ints2octs, null + from pyasn1.error import PyAsn1Error + +@@ -72,6 +73,25 @@ class OctetStringDecoderTestCase(BaseTestCase): + assert 0, 'chunked encoding tolerated' + + ++class RealDecoderTestCase(BaseTestCase): ++ def testCanonicalLargeBinaryReal(self): ++ substrate = encoder.encode(univ.Real((1, 2, 1000000))) ++ assert substrate == ints2octs((9, 5, 0x82, 0x0f, 0x42, 0x40, 1)) ++ ++ value, rest = decoder.decode(substrate) ++ ++ assert tuple(value) == (1, 2, 1000000) ++ assert rest == null ++ ++ def testLargeBinaryRoundTrip(self): ++ substrate = encoder.encode(univ.Real((-1, 2, 76354972))) ++ value, rest = decoder.decode(substrate) ++ ++ assert tuple(value) == (-1, 2, 76354972) ++ assert rest == null ++ ++ ++ + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + + if __name__ == '__main__': +diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py +index f5124df..3453d11 100644 +--- a/tests/type/test_univ.py ++++ b/tests/type/test_univ.py +@@ -632,9 +632,49 @@ class RealTestCase(BaseTestCase): + def testFloat(self): + assert float(univ.Real(4.0)) == 4.0, '__float__() fails' + ++ def testFloatBase10Precision(self): ++ assert float(univ.Real((3, 10, 23))) == 3e23, '__float__() lost base-10 behavior' ++ ++ def testFloatOverflow(self): ++ try: ++ float(univ.Real((1, 2, 1000000))) ++ except OverflowError: ++ pass ++ else: ++ assert 0, '__float__() tolerated overflow' ++ ++ assert univ.Real((1, 2, 1000000)).prettyPrint() == '' ++ ++ def testFloatUnderflow(self): ++ assert float(univ.Real((1, 2, -1000000))) == 0.0, '__float__() failed underflow' ++ ++ def testFloatZeroMantissa(self): ++ assert float(univ.Real((0, 10, 1000000000))) == 0.0, '__float__() failed zero mantissa' ++ assert float(univ.Real((0, 2, 1000000000))) == 0.0, '__float__() failed zero mantissa' ++ ++ def testFloatBase10Overflow(self): ++ try: ++ float(univ.Real((1, 10, sys.float_info.max_10_exp + 1))) ++ except OverflowError: ++ pass ++ else: ++ assert 0, '__float__() tolerated base-10 overflow' ++ ++ def testFloatBase10NormalizedOverflow(self): ++ try: ++ float(univ.Real((10, 10, sys.float_info.max_10_exp))) ++ except OverflowError: ++ pass ++ else: ++ assert 0, '__float__() tolerated normalized base-10 overflow' ++ + def testPrettyIn(self): + assert univ.Real((3, 10, 0)) == 3, 'prettyIn() fails' + ++ def testPrettyInBigBase10Mantissa(self): ++ assert tuple(univ.Real((10 ** 400, 10, 0))) == (1, 10, 400), \ ++ 'prettyIn() big mantissa normalization fails' ++ + # infinite float values + def testStrInf(self): + assert str(univ.Real('inf')) == 'inf', 'str() fails' diff --git a/python-pyasn1.spec b/python-pyasn1.spec index ff458c5..96188a1 100644 --- a/python-pyasn1.spec +++ b/python-pyasn1.spec @@ -3,7 +3,7 @@ Name: python-pyasn1 Version: 0.3.7 -Release: 6%{?dist}.1 +Release: 6%{?dist}.2 Summary: ASN.1 tools for Python License: BSD Group: System Environment/Libraries @@ -13,6 +13,8 @@ URL: http://pyasn1.sourceforge.net/ BuildArch: noarch Patch1: 0001-Backport-commit-be353d7.patch +# https://github.com/pyasn1/pyasn1/commit/90d5e1723913d756c23709bb90feb6f9e8bf585f +Patch2: python-pyasn1-0.3.7-CVE-2026-59886.patch %description @@ -89,6 +91,11 @@ PYTHONPATH=%{buildroot}%{python3_sitelib} %{__python3} setup.py test %doc doc/build/html/* %changelog +* Wed Jul 29 2026 RHEL Packaging Agent - 0.3.7-6.2 +- Fix denial of service in Real type __float__() method + (CVE-2026-59886) +- Resolves: RHEL-217908 + * Wed Feb 18 2026 Masahiro Matsuya - 0.3.7-6.el8_10.1 - Resolves: RHEL-148145