The univ.Real type converts its mantissa, base, and exponent to a Python
float using exact big-integer exponentiation. A BER, CER, or DER encoded
REAL value only a few bytes long can carry a very large exponent, causing
float conversion through prettyPrint(), str(), comparison, arithmetic,
int(), or an explicit float() call to consume excessive CPU and memory and
hang applications that decode untrusted ASN.1 data and then print, log, or
compare the decoded objects.

scarthgap ships pyasn1 0.5.1, which is affected as the issue is present in
all versions before 0.6.4.

Pick the upstream patch [1] as mentioned in [2].

[1] 
https://github.com/pyasn1/pyasn1/commit/e60c691cb91addb8fcefa2f537e85ede6fb1e886
[2] https://nvd.nist.gov/vuln/detail/CVE-2026-59886

Signed-off-by: Emily Vekariya <[email protected]>
---
 .../recipes-devtools/python/python-pyasn1.inc |   1 +
 .../python3-pyasn1/CVE-2026-59886.patch       | 252 ++++++++++++++++++
 2 files changed, 253 insertions(+)
 create mode 100644 
meta/recipes-devtools/python/python3-pyasn1/CVE-2026-59886.patch

diff --git a/meta/recipes-devtools/python/python-pyasn1.inc 
b/meta/recipes-devtools/python/python-pyasn1.inc
index 96b4a3b52a..1780ee1d88 100644
--- a/meta/recipes-devtools/python/python-pyasn1.inc
+++ b/meta/recipes-devtools/python/python-pyasn1.inc
@@ -19,6 +19,7 @@ inherit ptest
 SRC_URI += " \
        file://run-ptest \
        file://CVE-2026-23490.patch \
+       file://CVE-2026-59886.patch \
 "
 
 RDEPENDS:${PN}-ptest += " \
diff --git a/meta/recipes-devtools/python/python3-pyasn1/CVE-2026-59886.patch 
b/meta/recipes-devtools/python/python3-pyasn1/CVE-2026-59886.patch
new file mode 100644
index 0000000000..80468c6a5e
--- /dev/null
+++ b/meta/recipes-devtools/python/python3-pyasn1/CVE-2026-59886.patch
@@ -0,0 +1,252 @@
+From 9b89b511a7284f17ef3a2de6d05fbf6030133abb Mon Sep 17 00:00:00 2001
+From: Simon Pichugin <[email protected]>
+Date: Wed, 8 Jul 2026 17:32:09 -0700
+Subject: [PATCH] Merge commit from fork
+
+CVE: CVE-2026-59886
+Upstream-Status: Backport 
[https://github.com/pyasn1/pyasn1/commit/e60c691cb91addb8fcefa2f537e85ede6fb1e886]
+
+(cherry picked from commit e60c691cb91addb8fcefa2f537e85ede6fb1e886)
+Signed-off-by: Emily Vekariya <[email protected]>
+---
+ pyasn1/type/univ.py             | 21 +++++++++----
+ tests/codec/ber/test_decoder.py | 53 +++++++++++++++++++++++++++------
+ tests/codec/cer/test_decoder.py | 10 +++++++
+ tests/codec/der/test_decoder.py | 19 ++++++++++++
+ tests/type/test_univ.py         | 40 +++++++++++++++++++++++++
+ 5 files changed, 129 insertions(+), 14 deletions(-)
+
+diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py
+index c5d0778..adff2df 100644
+--- a/pyasn1/type/univ.py
++++ b/pyasn1/type/univ.py
+@@ -1318,7 +1318,7 @@ class Real(base.SimpleAsn1Type):
+     def __normalizeBase10(value):
+         m, b, e = value
+         while m and m % 10 == 0:
+-            m /= 10
++            m //= 10
+             e += 1
+         return m, b, e
+ 
+@@ -1457,10 +1457,21 @@ class Real(base.SimpleAsn1Type):
+     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 f033dfd..f6ff7b0 100644
+--- a/tests/codec/ber/test_decoder.py
++++ b/tests/codec/ber/test_decoder.py
+@@ -21,6 +21,7 @@ from pyasn1.type import univ
+ from pyasn1.type import char
+ from pyasn1.codec import streaming
+ from pyasn1.codec.ber import decoder
++from pyasn1.codec.ber import encoder
+ from pyasn1.codec.ber import eoo
+ from pyasn1.compat.octets import ints2octs, str2octs, null
+ from pyasn1 import error
+@@ -547,17 +548,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(
++            bytes((9, 5, 162, 0, 255, 255, 1))
++        )
++
++        assert tuple(value) == (1, 2, 262140)
++        assert rest == b''
++
++    def testBin7(self):  # large exponent in 4-octet form, base = 16
++        value, rest = decoder.decode(
++            bytes((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 == b''
++
++    def testLargeBinaryRoundTrip(self):
++        substrate = encoder.encode(univ.Real((-1, 2, 76354972)))
++        value, rest = decoder.decode(substrate)
++
++        assert tuple(value) == (-1, 2, 76354972)
++        assert rest == b''
++
++    def testLongFormBinaryRealExponentLength(self):
++        value, rest = decoder.decode(
++            bytes((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 == b''
++
++    def testLargeBinaryPrettyPrintOverflow(self):
++        value, rest = decoder.decode(
++            b'\t\t\xeb\x060662.666\xd0B\x00\x00\x00\x00\x00\x00\x00'
++        )
++
++        assert value.prettyPrint() == '<overflow>'
++        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 133affd..3d27194 100644
+--- a/tests/codec/cer/test_decoder.py
++++ b/tests/codec/cer/test_decoder.py
+@@ -15,6 +15,7 @@ from pyasn1.type import opentype
+ from pyasn1.type import univ
+ from pyasn1.codec.cer import decoder
+ from pyasn1.compat.octets import ints2octs, str2octs, null
++from pyasn1.codec.cer import encoder
+ from pyasn1.error import PyAsn1Error
+ 
+ 
+@@ -66,6 +67,15 @@ 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 == b''
++
++
+ class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase):
+     def setUp(self):
+         openType = opentype.OpenType(
+diff --git a/tests/codec/der/test_decoder.py b/tests/codec/der/test_decoder.py
+index 5bc9deb..553563c 100644
+--- a/tests/codec/der/test_decoder.py
++++ b/tests/codec/der/test_decoder.py
+@@ -15,6 +15,7 @@ from pyasn1.type import opentype
+ from pyasn1.type import univ
+ from pyasn1.codec.der import decoder
+ from pyasn1.compat.octets import ints2octs, null
++from pyasn1.codec.der import encoder
+ from pyasn1.error import PyAsn1Error
+ 
+ 
+@@ -72,6 +73,24 @@ class OctetStringDecoderTestCase(BaseTestCase):
+             assert 0, 'chunked encoding tolerated'
+ 
+ 
++class RealDecoderTestCase(BaseTestCase):
++    def testCanonicalLargeBinaryReal(self):
++        substrate = encoder.encode(univ.Real((1, 2, 1000000)))
++        assert substrate == bytes((9, 5, 0x82, 0x0f, 0x42, 0x40, 1))
++
++        value, rest = decoder.decode(substrate)
++
++        assert tuple(value) == (1, 2, 1000000)
++        assert rest == b''
++
++    def testLargeBinaryRoundTrip(self):
++        substrate = encoder.encode(univ.Real((-1, 2, 76354972)))
++        value, rest = decoder.decode(substrate)
++
++        assert tuple(value) == (-1, 2, 76354972)
++        assert rest == b''
++
++
+ class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase):
+     def setUp(self):
+         openType = opentype.OpenType(
+diff --git a/tests/type/test_univ.py b/tests/type/test_univ.py
+index 8aec183..bc21c37 100644
+--- a/tests/type/test_univ.py
++++ b/tests/type/test_univ.py
+@@ -780,9 +780,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() == '<overflow>'
++
++    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'
+-- 
+2.34.1
+
-- 
2.35.6

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#243950): 
https://lists.openembedded.org/g/openembedded-core/message/243950
Mute This Topic: https://lists.openembedded.org/mt/120866101/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

  • [OE-core][scarthgap][PATCH 1/2] ... Emily Vekariya via lists.openembedded.org

Reply via email to