On Mon Apr 27, 2026 at 8:04 AM CEST, Jiaying (CN) Song wrote: > Hi Yoann, > > This patch is for scarthgap where pyasn1 is at 0.5.1. The 0.6.2 -> 0.6.3 > upgrade would only apply to master. According to > https://github.com/pyasn1/pyasn1/security/advisories/GHSA-jr27-m4p2-rc6r , > all versions <= 0.6.2 are affected, so for scarthgap, this patch is still > needed to fix the CVE.
Yes, agreed but the trouble was that is was not fixed on master and, therefore, could not be fixed on scarthgap. Now the situation has changed, it is fixed on master by upgrading to 0.6.3 but wrynose is now vulnerable. Can you send a fix to wrynose? I can't merge a fix to scarthgap without one for wrynose. Thanks! > > Best regards, > Jiaying. > -----Original Message----- > From: [email protected] > <[email protected]> On Behalf Of Yoann Congal via > lists.openembedded.org > Sent: Friday, April 24, 2026 3:36 PM > To: Wang, Jinfeng (CN) <[email protected]>; > [email protected] > Subject: Re: [OE-core] [scarthgap][PATCH 09/12] python3-pyasn1: fix > CVE-2026-30922 > > CAUTION: This email comes from a non Wind River email account! > Do not click links or open attachments unless you recognize the sender and > know the content is safe. > > On Thu Apr 9, 2026 at 8:16 AM CEST, Jinfeng (CN) via lists.openembedded.org > Wang wrote: >> From: Jiaying Song <[email protected]> >> >> pyasn1 is a generic ASN.1 library for Python. Prior to 0.6.3, the >> `pyasn1` library is vulnerable to a Denial of Service (DoS) attack >> caused by uncontrolled recursion when decoding ASN.1 data with deeply >> nested structures. An attacker can supply a crafted payload containing >> thousands of nested `SEQUENCE` (`0x30`) or `SET` (`0x31`) tags with >> "Indefinite Length" (`0x80`) markers. This forces the decoder to >> recursively call itself until the Python interpreter crashes with a >> `RecursionError` or consumes all available memory (OOM), crashing the >> host application. This is a distinct vulnerability from CVE-2026-23490 >> (which addressed integer overflows in OID decoding). The fix for >> CVE-2026-23490 (`MAX_OID_ARC_CONTINUATION_OCTETS`) does not mitigate >> this recursion issue. Version 0.6.3 fixes this specific issue. >> >> References: >> https://nvd.nist.gov/vuln/detail/CVE-2026-30922 >> >> Signed-off-by: Jiaying Song <[email protected]> >> Signed-off-by: Jinfeng Wang <[email protected]> >> --- > > AFAIK, this CVE also apply to master (I'm not sure why it does not appear on > https://valkyrie.yocto.io/pub/non-release/patchmetrics/) > > I'll hold this patch until "python3-pyasn1: upgrade 0.6.2 -> 0.6.3" > merges. > >> .../recipes-devtools/python/python-pyasn1.inc | 1 + >> .../python3-pyasn1/CVE-2026-30922.patch | 257 ++++++++++++++++++ >> 2 files changed, 258 insertions(+) >> create mode 100644 >> meta/recipes-devtools/python/python3-pyasn1/CVE-2026-30922.patch >> >> diff --git a/meta/recipes-devtools/python/python-pyasn1.inc >> b/meta/recipes-devtools/python/python-pyasn1.inc >> index 96b4a3b52a..d69cdf8877 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-30922.patch \ >> " >> >> RDEPENDS:${PN}-ptest += " \ >> diff --git >> a/meta/recipes-devtools/python/python3-pyasn1/CVE-2026-30922.patch >> b/meta/recipes-devtools/python/python3-pyasn1/CVE-2026-30922.patch >> new file mode 100644 >> index 0000000000..7eceaa2595 >> --- /dev/null >> +++ b/meta/recipes-devtools/python/python3-pyasn1/CVE-2026-30922.patch >> @@ -0,0 +1,257 @@ >> +From 85e901d1dacdcd17363cc2dd18a91cfb72363eeb Mon Sep 17 00:00:00 >> +2001 >> +From: Simon Pichugin <[email protected]> >> +Date: Thu, 19 Mar 2026 17:11:40 +0800 >> +Subject: [PATCH] Merge commit from fork >> + >> +CVE: CVE-2026-30922 >> + >> +Upstream-Status: Backport >> +[https://github.com/pyasn1/pyasn1/commit/25ad481c19] >> + >> +Signed-off-by: Jiaying Song <[email protected]> >> +--- >> + pyasn1/codec/ber/decoder.py | 10 +++ >> + tests/codec/ber/test_decoder.py | 114 >> +++++++++++++++++++++++++++++++++ tests/codec/cer/test_decoder.py | >> +22 ++++++ tests/codec/der/test_decoder.py | 40 +++++++++++ >> + 4 files changed, 186 insertions(+) >> + >> +diff --git a/pyasn1/codec/ber/decoder.py >> +b/pyasn1/codec/ber/decoder.py index be8ba65..da2a048 100644 >> +--- a/pyasn1/codec/ber/decoder.py >> ++++ b/pyasn1/codec/ber/decoder.py >> +@@ -38,6 +38,7 @@ 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 >> ++MAX_NESTING_DEPTH = 100 >> + >> + >> + class AbstractPayloadDecoder(object): >> +@@ -1515,6 +1516,15 @@ class SingleItemDecoder(object): >> + decodeFun=None, substrateFun=None, >> + **options): >> + >> ++ _nestingLevel = options.get('_nestingLevel', 0) >> ++ >> ++ if _nestingLevel > MAX_NESTING_DEPTH: >> ++ raise error.PyAsn1Error( >> ++ 'ASN.1 structure nesting depth exceeds limit (%d)' % >> MAX_NESTING_DEPTH >> ++ ) >> ++ >> ++ options['_nestingLevel'] = _nestingLevel + 1 >> ++ >> + allowEoo = options.pop('allowEoo', False) >> + >> + if LOG: >> +diff --git a/tests/codec/ber/test_decoder.py >> +b/tests/codec/ber/test_decoder.py index f033dfd..226381a 100644 >> +--- a/tests/codec/ber/test_decoder.py >> ++++ b/tests/codec/ber/test_decoder.py >> +@@ -1987,6 +1987,120 @@ class CompressedFilesTestCase(BaseTestCase): >> + finally: >> + os.remove(path) >> + >> ++class NestingDepthLimitTestCase(BaseTestCase): >> ++ """Test protection against deeply nested ASN.1 structures (CVE >> prevention).""" >> ++ >> ++ def testIndefLenSequenceNesting(self): >> ++ """Deeply nested indefinite-length SEQUENCEs must raise >> PyAsn1Error.""" >> ++ # Each \x30\x80 opens a new indefinite-length SEQUENCE >> ++ payload = b'\x30\x80' * 200 >> ++ try: >> ++ decoder.decode(payload) >> ++ except error.PyAsn1Error: >> ++ pass >> ++ else: >> ++ assert False, 'Deeply nested indef-length SEQUENCEs not >> rejected' >> ++ >> ++ def testIndefLenSetNesting(self): >> ++ """Deeply nested indefinite-length SETs must raise PyAsn1Error.""" >> ++ # Each \x31\x80 opens a new indefinite-length SET >> ++ payload = b'\x31\x80' * 200 >> ++ try: >> ++ decoder.decode(payload) >> ++ except error.PyAsn1Error: >> ++ pass >> ++ else: >> ++ assert False, 'Deeply nested indef-length SETs not rejected' >> ++ >> ++ def testDefiniteLenNesting(self): >> ++ """Deeply nested definite-length SEQUENCEs must raise >> PyAsn1Error.""" >> ++ inner = b'\x05\x00' # NULL >> ++ for _ in range(200): >> ++ length = len(inner) >> ++ if length < 128: >> ++ inner = b'\x30' + bytes([length]) + inner >> ++ else: >> ++ length_bytes = length.to_bytes( >> ++ (length.bit_length() + 7) // 8, 'big') >> ++ inner = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ >> ++ length_bytes + inner >> ++ try: >> ++ decoder.decode(inner) >> ++ except error.PyAsn1Error: >> ++ pass >> ++ else: >> ++ assert False, 'Deeply nested definite-length SEQUENCEs not >> rejected' >> ++ >> ++ def testNestingUnderLimitWorks(self): >> ++ """Nesting within the limit must decode successfully.""" >> ++ inner = b'\x05\x00' # NULL >> ++ for _ in range(50): >> ++ length = len(inner) >> ++ if length < 128: >> ++ inner = b'\x30' + bytes([length]) + inner >> ++ else: >> ++ length_bytes = length.to_bytes( >> ++ (length.bit_length() + 7) // 8, 'big') >> ++ inner = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ >> ++ length_bytes + inner >> ++ asn1Object, _ = decoder.decode(inner) >> ++ assert asn1Object is not None, 'Valid nested structure rejected' >> ++ >> ++ def testSiblingsDontIncreaseDepth(self): >> ++ """Sibling elements at the same level must not inflate depth >> count.""" >> ++ # SEQUENCE containing 200 INTEGER siblings - should decode fine >> ++ components = b'\x02\x01\x01' * 200 # 200 x INTEGER(1) >> ++ length = len(components) >> ++ length_bytes = length.to_bytes( >> ++ (length.bit_length() + 7) // 8, 'big') >> ++ payload = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ >> ++ length_bytes + components >> ++ asn1Object, _ = decoder.decode(payload) >> ++ assert asn1Object is not None, 'Siblings incorrectly rejected' >> ++ >> ++ def testErrorMessageContainsLimit(self): >> ++ """Error message must indicate the nesting depth limit.""" >> ++ payload = b'\x30\x80' * 200 >> ++ try: >> ++ decoder.decode(payload) >> ++ except error.PyAsn1Error as exc: >> ++ assert 'nesting depth' in str(exc).lower(), \ >> ++ 'Error message missing depth info: %s' % exc >> ++ else: >> ++ assert False, 'Expected PyAsn1Error' >> ++ >> ++ def testNoRecursionError(self): >> ++ """Must raise PyAsn1Error, not RecursionError.""" >> ++ payload = b'\x30\x80' * 50000 >> ++ try: >> ++ decoder.decode(payload) >> ++ except error.PyAsn1Error: >> ++ pass >> ++ except RecursionError: >> ++ assert False, 'Got RecursionError instead of PyAsn1Error' >> ++ >> ++ def testMixedNesting(self): >> ++ """Mixed SEQUENCE and SET nesting must be caught.""" >> ++ # Alternate SEQUENCE (0x30) and SET (0x31) with indef length >> ++ payload = b'' >> ++ for i in range(200): >> ++ payload += b'\x30\x80' if i % 2 == 0 else b'\x31\x80' >> ++ try: >> ++ decoder.decode(payload) >> ++ except error.PyAsn1Error: >> ++ pass >> ++ else: >> ++ assert False, 'Mixed nesting not rejected' >> ++ >> ++ def testWithSchema(self): >> ++ """Deeply nested structures must be caught even with schema.""" >> ++ payload = b'\x30\x80' * 200 >> ++ try: >> ++ decoder.decode(payload, asn1Spec=univ.Sequence()) >> ++ except error.PyAsn1Error: >> ++ pass >> ++ else: >> ++ assert False, 'Deeply nested with schema not rejected' >> + >> + class NonStreamingCompatibilityTestCase(BaseTestCase): >> + def setUp(self): >> +diff --git a/tests/codec/cer/test_decoder.py >> +b/tests/codec/cer/test_decoder.py index 133affd..fbb1145 100644 >> +--- a/tests/codec/cer/test_decoder.py >> ++++ b/tests/codec/cer/test_decoder.py >> +@@ -363,6 +363,28 @@ class >> SequenceDecoderWithExplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): >> + assert s[0] == 3 >> + assert s[1][0] == univ.OctetString(hexValue='02010C') >> + >> ++class NestingDepthLimitTestCase(BaseTestCase): >> ++ """Test CER decoder protection against deeply nested structures.""" >> ++ >> ++ def testIndefLenNesting(self): >> ++ """Deeply nested indefinite-length SEQUENCEs must raise >> PyAsn1Error.""" >> ++ payload = b'\x30\x80' * 200 >> ++ try: >> ++ decoder.decode(payload) >> ++ except PyAsn1Error: >> ++ pass >> ++ else: >> ++ assert False, 'Deeply nested indef-length SEQUENCEs not >> rejected' >> ++ >> ++ def testNoRecursionError(self): >> ++ """Must raise PyAsn1Error, not RecursionError.""" >> ++ payload = b'\x30\x80' * 50000 >> ++ try: >> ++ decoder.decode(payload) >> ++ except PyAsn1Error: >> ++ pass >> ++ except RecursionError: >> ++ assert False, 'Got RecursionError instead of PyAsn1Error' >> + >> + suite = >> + unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) >> + >> +diff --git a/tests/codec/der/test_decoder.py >> +b/tests/codec/der/test_decoder.py index 5bc9deb..b0fa867 100644 >> +--- a/tests/codec/der/test_decoder.py >> ++++ b/tests/codec/der/test_decoder.py >> +@@ -361,6 +361,46 @@ class >> SequenceDecoderWithExplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): >> + assert s[0] == 3 >> + assert s[1][0] == univ.OctetString(hexValue='02010C') >> + >> ++class NestingDepthLimitTestCase(BaseTestCase): >> ++ """Test DER decoder protection against deeply nested structures.""" >> ++ >> ++ def testDefiniteLenNesting(self): >> ++ """Deeply nested definite-length SEQUENCEs must raise >> PyAsn1Error.""" >> ++ inner = b'\x05\x00' # NULL >> ++ for _ in range(200): >> ++ length = len(inner) >> ++ if length < 128: >> ++ inner = b'\x30' + bytes([length]) + inner >> ++ else: >> ++ length_bytes = length.to_bytes( >> ++ (length.bit_length() + 7) // 8, 'big') >> ++ inner = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ >> ++ length_bytes + inner >> ++ try: >> ++ decoder.decode(inner) >> ++ except PyAsn1Error: >> ++ pass >> ++ else: >> ++ assert False, 'Deeply nested definite-length SEQUENCEs not >> rejected' >> ++ >> ++ def testNoRecursionError(self): >> ++ """Must raise PyAsn1Error, not RecursionError.""" >> ++ inner = b'\x05\x00' >> ++ for _ in range(200): >> ++ length = len(inner) >> ++ if length < 128: >> ++ inner = b'\x30' + bytes([length]) + inner >> ++ else: >> ++ length_bytes = length.to_bytes( >> ++ (length.bit_length() + 7) // 8, 'big') >> ++ inner = b'\x30' + bytes([0x80 | len(length_bytes)]) + \ >> ++ length_bytes + inner >> ++ try: >> ++ decoder.decode(inner) >> ++ except PyAsn1Error: >> ++ pass >> ++ except RecursionError: >> ++ assert False, 'Got RecursionError instead of PyAsn1Error' >> + >> + suite = >> + unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) >> + >> +-- >> +2.34.1 >> + > > > -- > Yoann Congal > Smile ECS -- Yoann Congal Smile ECS
-=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#236627): https://lists.openembedded.org/g/openembedded-core/message/236627 Mute This Topic: https://lists.openembedded.org/mt/118738401/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
