Bug#1145581: trixie-pu: package pyasn1/0.6.1-1+deb13u3

Emmanuel Arias <[email protected]>
Newsgroups gmane.linux.debian.devel.release
Message-ID <178768286993.98918.17796882582121035627.reportbug__20373.5323668056$1787683063$gmane$org@debian>
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:pyasn1
User: [email protected]
Usertags: pu


[ Reason ]
Fixing CVE-2026-59884, CVE-2026-59885 and, CVE-2026-59886

[ Impact ]
Denial of service, cpu and memory exhaustion

[ Tests ]
Build it in debusine https://debusine.debian.net/debian/developers/work-request/1108490/

[ Risks ]
No issues as I can see, patches are simple, upstream added unittests.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable
pyasn1-trixie-debdiff.txt (text/plain, 32 KB)
diff -Nru pyasn1-0.6.1/debian/changelog pyasn1-0.6.1/debian/changelog
--- pyasn1-0.6.1/debian/changelog	2026-03-26 16:21:15.000000000 +0000
+++ pyasn1-0.6.1/debian/changelog	2026-08-24 20:02:31.000000000 +0000
@@ -1,3 +1,21 @@
+pyasn1 (0.6.1-1+deb13u3) trixie; urgency=high
+
+  * Team upload.
+  * CVE-2026-59886: uncontrolled resource consumption when converting
+    decoded real values. univ.Real convertedts  mantissa, base,
+    exponens to a Python float using exact big-integer exponentiation,
+    so a real value only a few bytes long could carry a very large exponent.
+  * CVE-2026-59884: BER/CER/DER decoder denial of service via unbounded
+    long-form tag IDs. The BER decoder accumulated tag continuation
+    octets without an upper bound, so a crafted substrate could force
+    construction of an arbitrarily large integer with quadratic CPU cost
+    (Closes: #1142388).
+  * CVE-2026-59885: fix quadratic complexity in OBJECT IDENTIFIER and
+    RELATIVE-OID decoding and encoding, which allowed denial of service
+    via a small payload with many arcs.
+
+ -- Emmanuel Arias <[email protected]>  Mon, 24 Aug 2026 17:02:31 -0300
+
 pyasn1 (0.6.1-1+deb13u2) trixie-security; urgency=high
 
   * Non-maintainer upload by the Security Team.
diff -Nru pyasn1-0.6.1/debian/patches/CVE-2026-59884.patch pyasn1-0.6.1/debian/patches/CVE-2026-59884.patch
--- pyasn1-0.6.1/debian/patches/CVE-2026-59884.patch	1970-01-01 00:00:00.000000000 +0000
+++ pyasn1-0.6.1/debian/patches/CVE-2026-59884.patch	2026-08-24 20:02:31.000000000 +0000
@@ -0,0 +1,240 @@
+From: Simon Pichugin <[email protected]>
+Date: Wed, 8 Jul 2026 17:36:30 -0700
+Subject: Merge commit from fork
+
+
+Origin: https://github.com/pyasn1/pyasn1/commit/628e36ecbb5277a3f01572ce418ef54271b165a5
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-59884
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-59884
+---
+ pyasn1/codec/ber/decoder.py     | 13 +++++++++++--
+ pyasn1/type/tag.py              | 20 ++++++++++++++++----
+ tests/codec/ber/test_decoder.py | 25 +++++++++++++++++++++++++
+ tests/codec/cer/test_decoder.py | 15 +++++++++++++++
+ tests/codec/der/test_decoder.py | 15 +++++++++++++++
+ tests/type/test_tag.py          | 20 ++++++++++++++++++++
+ 6 files changed, 102 insertions(+), 6 deletions(-)
+
+diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py
+index b8a8bd5..b1176c0 100644
+--- a/pyasn1/codec/ber/decoder.py
++++ b/pyasn1/codec/ber/decoder.py
+@@ -36,6 +36,10 @@ SubstrateUnderrunError = error.SubstrateUnderrunError
+ # 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
++
++# Maximum number of octets in a long-form tag ID (20 octets = up to
++# 140-bit tag IDs, matching the OID arc limit)
++MAX_TAG_OCTETS = 20
+ MAX_NESTING_DEPTH = 100
+ 
+ 
+@@ -1632,7 +1636,7 @@ class SingleItemDecoder(object):
+ 
+                     if tagId == 0x1F:
+                         isShortTag = False
+-                        lengthOctetIdx = 0
++                        tagOctetCount = 0
+                         tagId = 0
+ 
+                         while True:
+@@ -1646,7 +1650,12 @@ class SingleItemDecoder(object):
+                                 )
+ 
+                             integerTag = ord(integerByte)
+-                            lengthOctetIdx += 1
++                            tagOctetCount += 1
++                            if tagOctetCount > MAX_TAG_OCTETS:
++                                raise error.PyAsn1Error(
++                                    'Tag ID octet count exceeds limit (%d)' % (
++                                        MAX_TAG_OCTETS,)
++                                )
+                             tagId <<= 7
+                             tagId |= (integerTag & 0x7F)
+ 
+diff --git a/pyasn1/type/tag.py b/pyasn1/type/tag.py
+index ccb8b00..28cd3fd 100644
+--- a/pyasn1/type/tag.py
++++ b/pyasn1/type/tag.py
+@@ -34,6 +34,16 @@ tagCategoryExplicit = 0x02
+ tagCategoryUntagged = 0x04
+ 
+ 
++def _tagIdToStr(tagId):
++    # Decimal rendering of a huge tag ID can exceed the interpreter's
++    # integer-to-string conversion limit (sys.get_int_max_str_digits(),
++    # Python 3.11+) and raise ValueError; hexadecimal is not limited
++    try:
++        return str(tagId)
++    except ValueError:
++        return hex(tagId)
++
++
+ class Tag(object):
+     """Create ASN.1 tag
+ 
+@@ -56,7 +66,8 @@ class Tag(object):
+     """
+     def __init__(self, tagClass, tagFormat, tagId):
+         if tagId < 0:
+-            raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId)
++            raise error.PyAsn1Error(
++                'Negative tag ID (%s) not allowed' % _tagIdToStr(tagId))
+         self.__tagClass = tagClass
+         self.__tagFormat = tagFormat
+         self.__tagId = tagId
+@@ -65,7 +76,7 @@ class Tag(object):
+ 
+     def __repr__(self):
+         representation = '[%s:%s:%s]' % (
+-            self.__tagClass, self.__tagFormat, self.__tagId)
++            self.__tagClass, self.__tagFormat, _tagIdToStr(self.__tagId))
+         return '<%s object, tag %s>' % (
+             self.__class__.__name__, representation)
+ 
+@@ -194,8 +205,9 @@ class TagSet(object):
+         self.__hash = hash(self.__superTagsClassId)
+ 
+     def __repr__(self):
+-        representation = '-'.join(['%s:%s:%s' % (x.tagClass, x.tagFormat, x.tagId)
+-                                   for x in self.__superTags])
++        representation = '-'.join(
++            ['%s:%s:%s' % (x.tagClass, x.tagFormat, _tagIdToStr(x.tagId))
++             for x in self.__superTags])
+         if representation:
+             representation = 'tags ' + representation
+         else:
+diff --git a/tests/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py
+index b2f0cd3..3a32fb1 100644
+--- a/tests/codec/ber/test_decoder.py
++++ b/tests/codec/ber/test_decoder.py
+@@ -41,6 +41,31 @@ class LargeTagDecoderTestCase(BaseTestCase):
+     def testLongTag(self):
+         assert decoder.decode(bytes((0x1f, 2, 1, 0)))[0].tagSet == univ.Integer.tagSet
+ 
++    def testVeryLongTagRoundTrip(self):
++        # (1 << 140) - 1 is the largest tag ID fitting the 20 octet limit
++        for tagId in (1 << 77, (1 << 140) - 1):
++            largeTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, tagId)
++            asn1Spec = univ.Integer().subtype(implicitTag=largeTag)
++            value = univ.Integer(1).subtype(implicitTag=largeTag)
++
++            decoded, rest = decoder.decode(encoder.encode(value), asn1Spec=asn1Spec)
++
++            assert rest == b''
++            assert decoded == 1
++
++    def testExcessiveLongTag(self):
++        # 1 << 140 is the smallest tag ID needing 21 octets, one over the limit
++        excessiveTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 140)
++        asn1Spec = univ.Integer().subtype(implicitTag=excessiveTag)
++        substrate = encoder.encode(univ.Integer(1).subtype(implicitTag=excessiveTag))
++
++        try:
++            decoder.decode(substrate, asn1Spec=asn1Spec)
++        except error.PyAsn1Error:
++            pass
++        else:
++            assert 0, 'excessive long tag tolerated'
++
+     def testTagsEquivalence(self):
+         integer = univ.Integer(2).subtype(implicitTag=tag.Tag(tag.tagClassContext, 0, 0))
+         assert decoder.decode(bytes((0x9f, 0x80, 0x00, 0x02, 0x01, 0x02)), asn1Spec=integer) == decoder.decode(
+diff --git a/tests/codec/cer/test_decoder.py b/tests/codec/cer/test_decoder.py
+index a35895c..c9cfea7 100644
+--- a/tests/codec/cer/test_decoder.py
++++ b/tests/codec/cer/test_decoder.py
+@@ -66,6 +66,21 @@ class OctetStringDecoderTestCase(BaseTestCase):
+     # TODO: test failures on short chunked and long unchunked substrate samples
+ 
+ 
++class LargeTagDecoderTestCase(BaseTestCase):
++    def testExcessiveLongTag(self):
++        # 1 << 140 is the smallest tag ID needing 21 octets, one over the limit
++        excessiveTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 140)
++        asn1Spec = univ.Integer().subtype(implicitTag=excessiveTag)
++        substrate = encoder.encode(univ.Integer(1).subtype(implicitTag=excessiveTag))
++
++        try:
++            decoder.decode(substrate, asn1Spec=asn1Spec)
++        except PyAsn1Error:
++            pass
++        else:
++            assert 0, 'excessive long tag tolerated'
++
++
+ class RealDecoderTestCase(BaseTestCase):
+     def testLargeBinaryRoundTrip(self):
+         substrate = encoder.encode(univ.Real((-1, 2, 76354972)))
+diff --git a/tests/codec/der/test_decoder.py b/tests/codec/der/test_decoder.py
+index 576025c..32be27b 100644
+--- a/tests/codec/der/test_decoder.py
++++ b/tests/codec/der/test_decoder.py
+@@ -72,6 +72,21 @@ class OctetStringDecoderTestCase(BaseTestCase):
+             assert 0, 'chunked encoding tolerated'
+ 
+ 
++class LargeTagDecoderTestCase(BaseTestCase):
++    def testExcessiveLongTag(self):
++        # 1 << 140 is the smallest tag ID needing 21 octets, one over the limit
++        excessiveTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 140)
++        asn1Spec = univ.Integer().subtype(implicitTag=excessiveTag)
++        substrate = encoder.encode(univ.Integer(1).subtype(implicitTag=excessiveTag))
++
++        try:
++            decoder.decode(substrate, asn1Spec=asn1Spec)
++        except PyAsn1Error:
++            pass
++        else:
++            assert 0, 'excessive long tag tolerated'
++
++
+ class RealDecoderTestCase(BaseTestCase):
+     def testCanonicalLargeBinaryReal(self):
+         substrate = encoder.encode(univ.Real((1, 2, 1000000)))
+diff --git a/tests/type/test_tag.py b/tests/type/test_tag.py
+index d0ffa07..ab9b8b1 100644
+--- a/tests/type/test_tag.py
++++ b/tests/type/test_tag.py
+@@ -9,6 +9,7 @@ import unittest
+ 
+ from tests.base import BaseTestCase
+ 
++from pyasn1 import error
+ from pyasn1.type import tag
+ 
+ 
+@@ -23,6 +24,19 @@ class TagReprTestCase(TagTestCaseBase):
+     def testRepr(self):
+         assert 'Tag' in repr(self.t1)
+ 
++    def testReprHugeTagId(self):
++        # must not hit the interpreter's int-to-str conversion limit
++        hugeTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 100000)
++        assert 'Tag' in repr(hugeTag)
++
++    def testNegativeHugeTagId(self):
++        try:
++            tag.Tag(tag.tagClassContext, tag.tagFormatSimple, -(1 << 100000))
++        except error.PyAsn1Error:
++            pass
++        else:
++            assert 0, 'negative tag ID tolerated'
++
+ 
+ class TagCmpTestCase(TagTestCaseBase):
+     def testCmp(self):
+@@ -54,6 +68,12 @@ class TagSetReprTestCase(TagSetTestCaseBase):
+     def testRepr(self):
+         assert 'TagSet' in repr(self.ts1)
+ 
++    def testReprHugeTagId(self):
++        # must not hit the interpreter's int-to-str conversion limit
++        hugeTagSet = self.ts1.tagImplicitly(
++            tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 100000))
++        assert 'TagSet' in repr(hugeTagSet)
++
+ 
+ class TagSetCmpTestCase(TagSetTestCaseBase):
+     def testCmp(self):
diff -Nru pyasn1-0.6.1/debian/patches/CVE-2026-59885.patch pyasn1-0.6.1/debian/patches/CVE-2026-59885.patch
--- pyasn1-0.6.1/debian/patches/CVE-2026-59885.patch	1970-01-01 00:00:00.000000000 +0000
+++ pyasn1-0.6.1/debian/patches/CVE-2026-59885.patch	2026-08-24 20:02:31.000000000 +0000
@@ -0,0 +1,289 @@
+From: Simon Pichugin <[email protected]>
+Date: Wed, 8 Jul 2026 17:37:40 -0700
+Subject: Merge commit from fork
+
+
+Origin: https://github.com/pyasn1/pyasn1/commit/45bdb19eb7df4b3780fe9c912c63e99bffc39dd9
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-59885
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-59885
+---
+ pyasn1/codec/ber/decoder.py     | 28 +++++++++++++++-------------
+ pyasn1/codec/ber/encoder.py     | 24 ++++++++++++------------
+ tests/codec/ber/test_decoder.py | 36 ++++++++++++++++++++++++++++++++++++
+ tests/codec/ber/test_encoder.py | 20 ++++++++++++++++++++
+ 4 files changed, 83 insertions(+), 25 deletions(-)
+
+diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py
+index 47da67c..b8a8bd5 100644
+--- a/pyasn1/codec/ber/decoder.py
++++ b/pyasn1/codec/ber/decoder.py
+@@ -420,14 +420,14 @@ class ObjectIdentifierPayloadDecoder(AbstractSimplePayloadDecoder):
+         if not chunk:
+             raise error.PyAsn1Error('Empty substrate')
+ 
+-        oid = ()
++        oid = []
+         index = 0
+         substrateLen = len(chunk)
+         while index < substrateLen:
+             subId = chunk[index]
+             index += 1
+             if subId < 128:
+-                oid += (subId,)
++                oid.append(subId)
+             elif subId > 128:
+                 # Construct subid from a number of octets
+                 nextSubId = subId
+@@ -443,11 +443,11 @@ class ObjectIdentifierPayloadDecoder(AbstractSimplePayloadDecoder):
+                     subId = (subId << 7) + (nextSubId & 0x7F)
+                     if index >= substrateLen:
+                         raise error.SubstrateUnderrunError(
+-                            'Short substrate for sub-OID past %s' % (oid,)
++                            'Short substrate for sub-OID past %s' % (tuple(oid),)
+                         )
+                     nextSubId = chunk[index]
+                     index += 1
+-                oid += ((subId << 7) + nextSubId,)
++                oid.append((subId << 7) + nextSubId)
+             elif subId == 128:
+                 # ASN.1 spec forbids leading zeros (0x80) in OID
+                 # encoding, tolerating it opens a vulnerability. See
+@@ -457,15 +457,17 @@ class ObjectIdentifierPayloadDecoder(AbstractSimplePayloadDecoder):
+ 
+         # Decode two leading arcs
+         if 0 <= oid[0] <= 39:
+-            oid = (0,) + oid
++            oid.insert(0, 0)
+         elif 40 <= oid[0] <= 79:
+-            oid = (1, oid[0] - 40) + oid[1:]
++            oid[0] -= 40
++            oid.insert(0, 1)
+         elif oid[0] >= 80:
+-            oid = (2, oid[0] - 80) + oid[1:]
++            oid[0] -= 80
++            oid.insert(0, 2)
+         else:
+             raise error.PyAsn1Error('Malformed first OID octet: %s' % chunk[0])
+ 
+-        yield self._createComponent(asn1Spec, tagSet, oid, **options)
++        yield self._createComponent(asn1Spec, tagSet, tuple(oid), **options)
+ 
+ 
+ class RelativeOIDPayloadDecoder(AbstractSimplePayloadDecoder):
+@@ -485,14 +487,14 @@ class RelativeOIDPayloadDecoder(AbstractSimplePayloadDecoder):
+         if not chunk:
+             raise error.PyAsn1Error('Empty substrate')
+ 
+-        reloid = ()
++        reloid = []
+         index = 0
+         substrateLen = len(chunk)
+         while index < substrateLen:
+             subId = chunk[index]
+             index += 1
+             if subId < 128:
+-                reloid += (subId,)
++                reloid.append(subId)
+             elif subId > 128:
+                 # Construct subid from a number of octets
+                 nextSubId = subId
+@@ -508,11 +510,11 @@ class RelativeOIDPayloadDecoder(AbstractSimplePayloadDecoder):
+                     subId = (subId << 7) + (nextSubId & 0x7F)
+                     if index >= substrateLen:
+                         raise error.SubstrateUnderrunError(
+-                            'Short substrate for sub-OID past %s' % (reloid,)
++                            'Short substrate for sub-OID past %s' % (tuple(reloid),)
+                         )
+                     nextSubId = chunk[index]
+                     index += 1
+-                reloid += ((subId << 7) + nextSubId,)
++                reloid.append((subId << 7) + nextSubId)
+             elif subId == 128:
+                 # ASN.1 spec forbids leading zeros (0x80) in OID
+                 # encoding, tolerating it opens a vulnerability. See
+@@ -520,7 +522,7 @@ class RelativeOIDPayloadDecoder(AbstractSimplePayloadDecoder):
+                 # page 7
+                 raise error.PyAsn1Error('Invalid octet 0x80 in RELATIVE-OID encoding')
+ 
+-        yield self._createComponent(asn1Spec, tagSet, reloid, **options)
++        yield self._createComponent(asn1Spec, tagSet, tuple(reloid), **options)
+ 
+ 
+ class RealPayloadDecoder(AbstractSimplePayloadDecoder):
+diff --git a/pyasn1/codec/ber/encoder.py b/pyasn1/codec/ber/encoder.py
+index d16fb1f..71bbdba 100644
+--- a/pyasn1/codec/ber/encoder.py
++++ b/pyasn1/codec/ber/encoder.py
+@@ -325,30 +325,30 @@ class ObjectIdentifierEncoder(AbstractItemEncoder):
+         else:
+             raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
+ 
+-        octets = ()
++        octets = []
+ 
+         # Cycle through subIds
+         for subOid in oid:
+             if 0 <= subOid <= 127:
+                 # Optimize for the common case
+-                octets += (subOid,)
++                octets.append(subOid)
+ 
+             elif subOid > 127:
+                 # Pack large Sub-Object IDs
+-                res = (subOid & 0x7f,)
++                res = [subOid & 0x7f]
+                 subOid >>= 7
+ 
+                 while subOid:
+-                    res = (0x80 | (subOid & 0x7f),) + res
++                    res.append(0x80 | (subOid & 0x7f))
+                     subOid >>= 7
+ 
+                 # Add packed Sub-Object ID to resulted Object ID
+-                octets += res
++                octets.extend(reversed(res))
+ 
+             else:
+                 raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value))
+ 
+-        return octets, False, False
++        return tuple(octets), False, False
+ 
+ 
+ class RelativeOIDEncoder(AbstractItemEncoder):
+@@ -358,30 +358,30 @@ class RelativeOIDEncoder(AbstractItemEncoder):
+         if asn1Spec is not None:
+             value = asn1Spec.clone(value)
+ 
+-        octets = ()
++        octets = []
+ 
+         # Cycle through subIds
+         for subOid in value.asTuple():
+             if 0 <= subOid <= 127:
+                 # Optimize for the common case
+-                octets += (subOid,)
++                octets.append(subOid)
+ 
+             elif subOid > 127:
+                 # Pack large Sub-Object IDs
+-                res = (subOid & 0x7f,)
++                res = [subOid & 0x7f]
+                 subOid >>= 7
+ 
+                 while subOid:
+-                    res = (0x80 | (subOid & 0x7f),) + res
++                    res.append(0x80 | (subOid & 0x7f))
+                     subOid >>= 7
+ 
+                 # Add packed Sub-Object ID to resulted RELATIVE-OID
+-                octets += res
++                octets.extend(reversed(res))
+ 
+             else:
+                 raise error.PyAsn1Error('Negative RELATIVE-OID arc %s at %s' % (subOid, value))
+ 
+-        return octets, False, False
++        return tuple(octets), False, False
+ 
+ 
+ class RealEncoder(AbstractItemEncoder):
+diff --git a/tests/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py
+index 4159212..b2f0cd3 100644
+--- a/tests/codec/ber/test_decoder.py
++++ b/tests/codec/ber/test_decoder.py
+@@ -26,6 +26,14 @@ from pyasn1.codec.ber import eoo
+ from pyasn1 import error
+ 
+ 
++def encode_length(length):
++    if length < 128:
++        return bytes([length])
++
++    lengthBytes = length.to_bytes((length.bit_length() + 7) // 8, 'big')
++    return bytes([0x80 | len(lengthBytes)]) + lengthBytes
++
++
+ class LargeTagDecoderTestCase(BaseTestCase):
+     def testLargeTag(self):
+         assert decoder.decode(bytes((127, 141, 245, 182, 253, 47, 3, 2, 1, 1))) == (1, b'')
+@@ -450,6 +458,20 @@ class ObjectIdentifierDecoderTestCase(BaseTestCase):
+             bytes((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47))
+         ) == ((2, 999, 18446744073709551535184467440737095), b'')
+ 
++    def testManySingleByteArcs(self):
++        encodedArcCount = 4096
++        substrate = (
++            bytes([0x06]) +
++            encode_length(encodedArcCount) +
++            bytes([0x01] * encodedArcCount)
++        )
++
++        value, rest = decoder.decode(substrate)
++        assert rest == b''
++        assert len(value) == encodedArcCount + 1
++        assert tuple(value[:3]) == (0, 1, 1)
++        assert tuple(value[-3:]) == (1, 1, 1)
++
+     def testExcessiveContinuationOctets(self):
+         """Test that OID arcs with excessive continuation octets are rejected."""
+         # Create a payload with 25 continuation octets (exceeds 20 limit)
+@@ -585,6 +607,20 @@ class RelativeOIDDecoderTestCase(BaseTestCase):
+             bytes((0x0D, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47))
+         ) == ((1079, 18446744073709551535184467440737095), b'')
+ 
++    def testManySingleByteArcs(self):
++        arcCount = 4096
++        substrate = (
++            bytes([0x0d]) +
++            encode_length(arcCount) +
++            bytes([0x01] * arcCount)
++        )
++
++        value, rest = decoder.decode(substrate)
++        assert rest == b''
++        assert len(value) == arcCount
++        assert tuple(value[:3]) == (1, 1, 1)
++        assert tuple(value[-3:]) == (1, 1, 1)
++
+     def testExcessiveContinuationOctets(self):
+         """Test that RELATIVE-OID arcs with excessive continuation octets are rejected."""
+         # Create a payload with 25 continuation octets (exceeds 20 limit)
+diff --git a/tests/codec/ber/test_encoder.py b/tests/codec/ber/test_encoder.py
+index 2bda716..6248423 100644
+--- a/tests/codec/ber/test_encoder.py
++++ b/tests/codec/ber/test_encoder.py
+@@ -348,6 +348,16 @@ class ObjectIdentifierEncoderTestCase(BaseTestCase):
+         ) == bytes((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6,
+                         0xB8, 0xCB, 0xE2, 0xB6, 0x47))
+ 
++    def testManySingleByteArcs(self):
++        arcCount = 4096
++        substrate = encoder.encode(
++            univ.ObjectIdentifier((1, 3) + (1,) * arcCount)
++        )
++
++        assert substrate == (
++            bytes([0x06, 0x82, 0x10, 0x01, 0x2B]) + bytes([0x01] * arcCount)
++        )
++
+ 
+ class ObjectIdentifierWithSchemaEncoderTestCase(BaseTestCase):
+     def testOne(self):
+@@ -379,6 +389,16 @@ class RelativeOIDEncoderTestCase(BaseTestCase):
+                         0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB,
+                         0xE2, 0xB6, 0x47))
+ 
++    def testManySingleByteArcs(self):
++        arcCount = 4096
++        substrate = encoder.encode(
++            univ.RelativeOID((1,) * arcCount)
++        )
++
++        assert substrate == (
++            bytes([0x0D, 0x82, 0x10, 0x00]) + bytes([0x01] * arcCount)
++        )
++
+ 
+ class RelativeOIDWithSchemaEncoderTestCase(BaseTestCase):
+     def testOne(self):
diff -Nru pyasn1-0.6.1/debian/patches/CVE-2026-59886.patch pyasn1-0.6.1/debian/patches/CVE-2026-59886.patch
--- pyasn1-0.6.1/debian/patches/CVE-2026-59886.patch	1970-01-01 00:00:00.000000000 +0000
+++ pyasn1-0.6.1/debian/patches/CVE-2026-59886.patch	2026-08-24 20:02:31.000000000 +0000
@@ -0,0 +1,247 @@
+From: Simon Pichugin <[email protected]>
+Date: Wed, 8 Jul 2026 17:32:09 -0700
+Subject: Merge commit from fork
+
+
+Origin: https://github.com/pyasn1/pyasn1/commit/e60c691cb91addb8fcefa2f537e85ede6fb1e886
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-59886
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-59886
+---
+ 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 9aff5e6..8b786dd 100644
+--- a/pyasn1/type/univ.py
++++ b/pyasn1/type/univ.py
+@@ -1362,7 +1362,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
+ 
+@@ -1490,10 +1490,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 3e0e09a..4159212 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 import error
+ 
+@@ -680,17 +681,51 @@ class RealDecoderTestCase(BaseTestCase):
+             bytes((9, 4, 161, 255, 1, 3))
+         ) == (univ.Real((3, 2, -1020)), b'')
+ 
+-# 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(
+-#            bytes((9, 5, 162, 0, 255, 255, 1))
+-#        ) == (univ.Real((1, 2, 262140)), b'')
++        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)
+ 
+-#    def testBin7(self):
+-#        assert decoder.decode(
+-#            bytes((9, 7, 227, 4, 1, 35, 69, 103, 1))
+-#        ) == (univ.Real((-1, 2, 76354972)), b'')
++        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))
++        )
++
++        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 24d1999..a35895c 100644
+--- a/tests/codec/cer/test_decoder.py
++++ b/tests/codec/cer/test_decoder.py
+@@ -14,6 +14,7 @@ from pyasn1.type import namedtype
+ from pyasn1.type import opentype
+ from pyasn1.type import univ
+ from pyasn1.codec.cer import decoder
++from pyasn1.codec.cer import encoder
+ from pyasn1.error import PyAsn1Error
+ 
+ 
+@@ -65,6 +66,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 ab24c07..576025c 100644
+--- a/tests/codec/der/test_decoder.py
++++ b/tests/codec/der/test_decoder.py
+@@ -14,6 +14,7 @@ from pyasn1.type import namedtype
+ from pyasn1.type import opentype
+ from pyasn1.type import univ
+ from pyasn1.codec.der import decoder
++from pyasn1.codec.der import encoder
+ from pyasn1.error import PyAsn1Error
+ 
+ 
+@@ -71,6 +72,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 c1e88c0..d905579 100644
+--- a/tests/type/test_univ.py
++++ b/tests/type/test_univ.py
+@@ -752,9 +752,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'
diff -Nru pyasn1-0.6.1/debian/patches/series pyasn1-0.6.1/debian/patches/series
--- pyasn1-0.6.1/debian/patches/series	2026-03-26 16:18:48.000000000 +0000
+++ pyasn1-0.6.1/debian/patches/series	2026-08-24 20:02:31.000000000 +0000
@@ -1,3 +1,6 @@
 0002-Remove-some-theme-options-to-avoid-needless-badges-i.patch
 CVE-2026-23490.patch
 CVE-2026-30922.patch
+CVE-2026-59886.patch
+CVE-2026-59884.patch
+CVE-2026-59885.patch
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.