From: "Yoann Congal" <yoann.congal@smile.fr>
To: <vanusuri@mvista.com>, <openembedded-core@lists.openembedded.org>
Subject: Re: [OE-core][kirkstone][PATCH] python3: upgrade 3.10.19 -> 3.10.20
Date: Tue, 07 Apr 2026 08:54:02 +0200 [thread overview]
Message-ID: <DHMQ4866I1F6.3ESGDXTBOPBT4@smile.fr> (raw)
In-Reply-To: <20260406134627.542578-1-vanusuri@mvista.com>
On Mon Apr 6, 2026 at 3:46 PM CEST, Vijay Anusuri via lists.openembedded.org wrote:
> From: Vijay Anusuri <vanusuri@mvista.com>
>
> Drop upstreamed patches.
>
> Release information:
> * https://www.python.org/downloads/release/python-31020/
> * The release you're looking at is Python 3.10.20, a security bugfix release for the legacy 3.10 series.
>
> Handles CVE-2024-6923 CVE-2025-6075 CVE-2025-12084 CVE-2025-13836 CVE-2025-13837
> CVE-2025-15282 CVE-2025-59375 CVE-2026-0865 CVE-2026-24515 CVE-2026-25210
>
> Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
> ---
Hello,
That succesfully passed testing:
https://autobuilder.yoctoproject.org/valkyrie/#/builders/29/builds/3606
So, I'll include it in my branch.
Thanks!
> .../python/python3/CVE-2025-12084.patch | 171 --------
> .../python/python3/CVE-2025-13836.patch | 163 --------
> .../python/python3/CVE-2025-13837.patch | 162 --------
> .../python/python3/CVE-2025-15282.patch | 68 ----
> .../python/python3/CVE-2025-6075.patch | 364 ------------------
> ...{python3_3.10.19.bb => python3_3.10.20.bb} | 7 +-
> 6 files changed, 1 insertion(+), 934 deletions(-)
> delete mode 100644 meta/recipes-devtools/python/python3/CVE-2025-12084.patch
> delete mode 100644 meta/recipes-devtools/python/python3/CVE-2025-13836.patch
> delete mode 100644 meta/recipes-devtools/python/python3/CVE-2025-13837.patch
> delete mode 100644 meta/recipes-devtools/python/python3/CVE-2025-15282.patch
> delete mode 100644 meta/recipes-devtools/python/python3/CVE-2025-6075.patch
> rename meta/recipes-devtools/python/{python3_3.10.19.bb => python3_3.10.20.bb} (98%)
>
> diff --git a/meta/recipes-devtools/python/python3/CVE-2025-12084.patch b/meta/recipes-devtools/python/python3/CVE-2025-12084.patch
> deleted file mode 100644
> index 0c9bb435ed..0000000000
> --- a/meta/recipes-devtools/python/python3/CVE-2025-12084.patch
> +++ /dev/null
> @@ -1,171 +0,0 @@
> -From c97e87593063d84a2bd9fe7068b30eb44de23dc0 Mon Sep 17 00:00:00 2001
> -From: "Miss Islington (bot)"
> - <31488909+miss-islington@users.noreply.github.com>
> -Date: Sun, 25 Jan 2026 18:10:49 +0100
> -Subject: [PATCH] [3.10] gh-142145: Remove quadratic behavior in node ID cache
> - clearing (GH-142146) (#142213)
> -
> -* gh-142145: Remove quadratic behavior in node ID cache clearing (GH-142146)
> -
> -* Remove quadratic behavior in node ID cache clearing
> -
> -Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com>
> -
> -* Add news fragment
> -
> -CVE: CVE-2025-12084
> -Upstream-Status: Backport [https://github.com/python/cpython/commit/c97e87593063d84a2bd9fe7068b30eb44de23dc0]
> -Signed-off-by: Peter Marko <peter.marko@siemens.com>
> ----------
> -(cherry picked from commit 08d8e18ad81cd45bc4a27d6da478b51ea49486e4)
> -
> -Co-authored-by: Seth Michael Larson <seth@python.org>
> -Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com>
> -
> -* [3.14] gh-142754: Ensure that Element & Attr instances have the ownerDocument attribute (GH-142794) (#142818)
> -
> -gh-142754: Ensure that Element & Attr instances have the ownerDocument attribute (GH-142794)
> -(cherry picked from commit 1cc7551b3f9f71efbc88d96dce90f82de98b2454)
> -
> -Co-authored-by: Petr Viktorin <encukou@gmail.com>
> -Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
> -
> -* gh-142145: relax the no-longer-quadratic test timing (GH-143030)
> -
> -* gh-142145: relax the no-longer-quadratic test timing
> -
> -* require cpu resource
> -(cherry picked from commit 8d2d7bb2e754f8649a68ce4116271a4932f76907)
> -
> -Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
> -
> -* merge NEWS entries into one
> -
> ----------
> -
> -Co-authored-by: Seth Michael Larson <seth@python.org>
> -Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com>
> -Co-authored-by: Petr Viktorin <encukou@gmail.com>
> -Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
> -Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
> -Co-authored-by: Gregory P. Smith <greg@krypto.org>
> ----
> - Lib/test/test_minidom.py | 33 ++++++++++++++++++-
> - Lib/xml/dom/minidom.py | 11 ++-----
> - ...-12-01-09-36-45.gh-issue-142145.tcAUhg.rst | 6 ++++
> - 3 files changed, 41 insertions(+), 9 deletions(-)
> - create mode 100644 Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
> -
> -diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py
> -index ef38c36210..c68bd990f7 100644
> ---- a/Lib/test/test_minidom.py
> -+++ b/Lib/test/test_minidom.py
> -@@ -2,6 +2,7 @@
> -
> - import copy
> - import pickle
> -+import time
> - import io
> - from test import support
> - import unittest
> -@@ -9,7 +10,7 @@ import unittest
> - import pyexpat
> - import xml.dom.minidom
> -
> --from xml.dom.minidom import parse, Attr, Node, Document, parseString
> -+from xml.dom.minidom import parse, Attr, Node, Document, Element, parseString
> - from xml.dom.minidom import getDOMImplementation
> - from xml.parsers.expat import ExpatError
> -
> -@@ -177,6 +178,36 @@ class MinidomTest(unittest.TestCase):
> - self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
> - dom.unlink()
> -
> -+ @support.requires_resource('cpu')
> -+ def testAppendChildNoQuadraticComplexity(self):
> -+ impl = getDOMImplementation()
> -+
> -+ newdoc = impl.createDocument(None, "some_tag", None)
> -+ top_element = newdoc.documentElement
> -+ children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)]
> -+ element = top_element
> -+
> -+ start = time.monotonic()
> -+ for child in children:
> -+ element.appendChild(child)
> -+ element = child
> -+ end = time.monotonic()
> -+
> -+ # This example used to take at least 30 seconds.
> -+ # Conservative assertion due to the wide variety of systems and
> -+ # build configs timing based tests wind up run under.
> -+ # A --with-address-sanitizer --with-pydebug build on a rpi5 still
> -+ # completes this loop in <0.5 seconds.
> -+ self.assertLess(end - start, 4)
> -+
> -+ def testSetAttributeNodeWithoutOwnerDocument(self):
> -+ # regression test for gh-142754
> -+ elem = Element("test")
> -+ attr = Attr("id")
> -+ attr.value = "test-id"
> -+ elem.setAttributeNode(attr)
> -+ self.assertEqual(elem.getAttribute("id"), "test-id")
> -+
> - def testAppendChildFragment(self):
> - dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
> - dom.documentElement.appendChild(frag)
> -diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py
> -index ef8a159833..cada981f39 100644
> ---- a/Lib/xml/dom/minidom.py
> -+++ b/Lib/xml/dom/minidom.py
> -@@ -292,13 +292,6 @@ def _append_child(self, node):
> - childNodes.append(node)
> - node.parentNode = self
> -
> --def _in_document(node):
> -- # return True iff node is part of a document tree
> -- while node is not None:
> -- if node.nodeType == Node.DOCUMENT_NODE:
> -- return True
> -- node = node.parentNode
> -- return False
> -
> - def _write_data(writer, data):
> - "Writes datachars to writer."
> -@@ -355,6 +348,7 @@ class Attr(Node):
> - def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,
> - prefix=None):
> - self.ownerElement = None
> -+ self.ownerDocument = None
> - self._name = qName
> - self.namespaceURI = namespaceURI
> - self._prefix = prefix
> -@@ -680,6 +674,7 @@ class Element(Node):
> -
> - def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
> - localName=None):
> -+ self.ownerDocument = None
> - self.parentNode = None
> - self.tagName = self.nodeName = tagName
> - self.prefix = prefix
> -@@ -1539,7 +1534,7 @@ def _clear_id_cache(node):
> - if node.nodeType == Node.DOCUMENT_NODE:
> - node._id_cache.clear()
> - node._id_search_stack = None
> -- elif _in_document(node):
> -+ elif node.ownerDocument:
> - node.ownerDocument._id_cache.clear()
> - node.ownerDocument._id_search_stack= None
> -
> -diff --git a/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst b/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
> -new file mode 100644
> -index 0000000000..05c7df35d1
> ---- /dev/null
> -+++ b/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
> -@@ -0,0 +1,6 @@
> -+Remove quadratic behavior in ``xml.minidom`` node ID cache clearing. In order
> -+to do this without breaking existing users, we also add the *ownerDocument*
> -+attribute to :mod:`xml.dom.minidom` elements and attributes created by directly
> -+instantiating the ``Element`` or ``Attr`` class. Note that this way of creating
> -+nodes is not supported; creator functions like
> -+:py:meth:`xml.dom.Document.documentElement` should be used instead.
> diff --git a/meta/recipes-devtools/python/python3/CVE-2025-13836.patch b/meta/recipes-devtools/python/python3/CVE-2025-13836.patch
> deleted file mode 100644
> index c4387b6019..0000000000
> --- a/meta/recipes-devtools/python/python3/CVE-2025-13836.patch
> +++ /dev/null
> @@ -1,163 +0,0 @@
> -From 289f29b0fe38baf2d7cb5854f4bb573cc34a6a15 Mon Sep 17 00:00:00 2001
> -From: "Miss Islington (bot)"
> - <31488909+miss-islington@users.noreply.github.com>
> -Date: Fri, 5 Dec 2025 16:21:57 +0100
> -Subject: [PATCH] [3.13] gh-119451: Fix a potential denial of service in
> - http.client (GH-119454) (#142139)
> -
> -gh-119451: Fix a potential denial of service in http.client (GH-119454)
> -
> -Reading the whole body of the HTTP response could cause OOM if
> -the Content-Length value is too large even if the server does not send
> -a large amount of data. Now the HTTP client reads large data by chunks,
> -therefore the amount of consumed memory is proportional to the amount
> -of sent data.
> -(cherry picked from commit 5a4c4a033a4a54481be6870aa1896fad732555b5)
> -
> -CVE: CVE-2025-13836
> -Upstream-Status: Backport [https://github.com/python/cpython/commit/289f29b0fe38baf2d7cb5854f4bb573cc34a6a15]
> -Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
> ----
> - Lib/http/client.py | 28 ++++++--
> - Lib/test/test_httplib.py | 66 +++++++++++++++++++
> - ...-05-23-11-47-48.gh-issue-119451.qkJe9-.rst | 5 ++
> - 3 files changed, 95 insertions(+), 4 deletions(-)
> - create mode 100644 Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
> -
> -diff --git a/Lib/http/client.py b/Lib/http/client.py
> -index d1b7b10..c8ab5b7 100644
> ---- a/Lib/http/client.py
> -+++ b/Lib/http/client.py
> -@@ -111,6 +111,11 @@ responses = {v: v.phrase for v in http.HTTPStatus.__members__.values()}
> - _MAXLINE = 65536
> - _MAXHEADERS = 100
> -
> -+# Data larger than this will be read in chunks, to prevent extreme
> -+# overallocation.
> -+_MIN_READ_BUF_SIZE = 1 << 20
> -+
> -+
> - # Header name/value ABNF (http://tools.ietf.org/html/rfc7230#section-3.2)
> - #
> - # VCHAR = %x21-7E
> -@@ -628,10 +633,25 @@ class HTTPResponse(io.BufferedIOBase):
> - reading. If the bytes are truly not available (due to EOF), then the
> - IncompleteRead exception can be used to detect the problem.
> - """
> -- data = self.fp.read(amt)
> -- if len(data) < amt:
> -- raise IncompleteRead(data, amt-len(data))
> -- return data
> -+ cursize = min(amt, _MIN_READ_BUF_SIZE)
> -+ data = self.fp.read(cursize)
> -+ if len(data) >= amt:
> -+ return data
> -+ if len(data) < cursize:
> -+ raise IncompleteRead(data, amt - len(data))
> -+
> -+ data = io.BytesIO(data)
> -+ data.seek(0, 2)
> -+ while True:
> -+ # This is a geometric increase in read size (never more than
> -+ # doubling out the current length of data per loop iteration).
> -+ delta = min(cursize, amt - cursize)
> -+ data.write(self.fp.read(delta))
> -+ if data.tell() >= amt:
> -+ return data.getvalue()
> -+ cursize += delta
> -+ if data.tell() < cursize:
> -+ raise IncompleteRead(data.getvalue(), amt - data.tell())
> -
> - def _safe_readinto(self, b):
> - """Same as _safe_read, but for reading into a buffer."""
> -diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
> -index 77152cf..89ec5f6 100644
> ---- a/Lib/test/test_httplib.py
> -+++ b/Lib/test/test_httplib.py
> -@@ -1226,6 +1226,72 @@ class BasicTest(TestCase):
> - thread.join()
> - self.assertEqual(result, b"proxied data\n")
> -
> -+ def test_large_content_length(self):
> -+ serv = socket.create_server((HOST, 0))
> -+ self.addCleanup(serv.close)
> -+
> -+ def run_server():
> -+ [conn, address] = serv.accept()
> -+ with conn:
> -+ while conn.recv(1024):
> -+ conn.sendall(
> -+ b"HTTP/1.1 200 Ok\r\n"
> -+ b"Content-Length: %d\r\n"
> -+ b"\r\n" % size)
> -+ conn.sendall(b'A' * (size//3))
> -+ conn.sendall(b'B' * (size - size//3))
> -+
> -+ thread = threading.Thread(target=run_server)
> -+ thread.start()
> -+ self.addCleanup(thread.join, 1.0)
> -+
> -+ conn = client.HTTPConnection(*serv.getsockname())
> -+ try:
> -+ for w in range(15, 27):
> -+ size = 1 << w
> -+ conn.request("GET", "/")
> -+ with conn.getresponse() as response:
> -+ self.assertEqual(len(response.read()), size)
> -+ finally:
> -+ conn.close()
> -+ thread.join(1.0)
> -+
> -+ def test_large_content_length_truncated(self):
> -+ serv = socket.create_server((HOST, 0))
> -+ self.addCleanup(serv.close)
> -+
> -+ def run_server():
> -+ while True:
> -+ [conn, address] = serv.accept()
> -+ with conn:
> -+ conn.recv(1024)
> -+ if not size:
> -+ break
> -+ conn.sendall(
> -+ b"HTTP/1.1 200 Ok\r\n"
> -+ b"Content-Length: %d\r\n"
> -+ b"\r\n"
> -+ b"Text" % size)
> -+
> -+ thread = threading.Thread(target=run_server)
> -+ thread.start()
> -+ self.addCleanup(thread.join, 1.0)
> -+
> -+ conn = client.HTTPConnection(*serv.getsockname())
> -+ try:
> -+ for w in range(18, 65):
> -+ size = 1 << w
> -+ conn.request("GET", "/")
> -+ with conn.getresponse() as response:
> -+ self.assertRaises(client.IncompleteRead, response.read)
> -+ conn.close()
> -+ finally:
> -+ conn.close()
> -+ size = 0
> -+ conn.request("GET", "/")
> -+ conn.close()
> -+ thread.join(1.0)
> -+
> - def test_putrequest_override_domain_validation(self):
> - """
> - It should be possible to override the default validation
> -diff --git a/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst b/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
> -new file mode 100644
> -index 0000000..6d6f25c
> ---- /dev/null
> -+++ b/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
> -@@ -0,0 +1,5 @@
> -+Fix a potential memory denial of service in the :mod:`http.client` module.
> -+When connecting to a malicious server, it could cause
> -+an arbitrary amount of memory to be allocated.
> -+This could have led to symptoms including a :exc:`MemoryError`, swapping, out
> -+of memory (OOM) killed processes or containers, or even system crashes.
> ---
> -2.50.1
> -
> diff --git a/meta/recipes-devtools/python/python3/CVE-2025-13837.patch b/meta/recipes-devtools/python/python3/CVE-2025-13837.patch
> deleted file mode 100644
> index 36bf75792b..0000000000
> --- a/meta/recipes-devtools/python/python3/CVE-2025-13837.patch
> +++ /dev/null
> @@ -1,162 +0,0 @@
> -From 5a8b19677d818fb41ee55f310233772e15aa1a2b Mon Sep 17 00:00:00 2001
> -From: Serhiy Storchaka <storchaka@gmail.com>
> -Date: Mon, 22 Dec 2025 15:49:44 +0200
> -Subject: [PATCH] [3.12] gh-119342: Fix a potential denial of service in
> - plistlib (GH-119343) (#142149)
> -
> -Reading a specially prepared small Plist file could cause OOM because file's
> -read(n) preallocates a bytes object for reading the specified amount of
> -data. Now plistlib reads large data by chunks, therefore the upper limit of
> -consumed memory is proportional to the size of the input file.
> -(cherry picked from commit 694922cf40aa3a28f898b5f5ee08b71b4922df70)
> -
> -CVE: CVE-2025-13837
> -Upstream-Status: Backport [https://github.com/python/cpython/commit/5a8b19677d818fb41ee55f310233772e15aa1a2b]
> -Signed-off-by: Peter Marko <peter.marko@siemens.com>
> ----
> - Lib/plistlib.py | 31 ++++++++++------
> - Lib/test/test_plistlib.py | 37 +++++++++++++++++--
> - ...-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst | 5 +++
> - 3 files changed, 59 insertions(+), 14 deletions(-)
> - create mode 100644 Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst
> -
> -diff --git a/Lib/plistlib.py b/Lib/plistlib.py
> -index 3292c30d5f..c5554ea1f7 100644
> ---- a/Lib/plistlib.py
> -+++ b/Lib/plistlib.py
> -@@ -73,6 +73,9 @@ from xml.parsers.expat import ParserCreate
> - PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
> - globals().update(PlistFormat.__members__)
> -
> -+# Data larger than this will be read in chunks, to prevent extreme
> -+# overallocation.
> -+_MIN_READ_BUF_SIZE = 1 << 20
> -
> - class UID:
> - def __init__(self, data):
> -@@ -499,12 +502,24 @@ class _BinaryPlistParser:
> -
> - return tokenL
> -
> -+ def _read(self, size):
> -+ cursize = min(size, _MIN_READ_BUF_SIZE)
> -+ data = self._fp.read(cursize)
> -+ while True:
> -+ if len(data) != cursize:
> -+ raise InvalidFileException
> -+ if cursize == size:
> -+ return data
> -+ delta = min(cursize, size - cursize)
> -+ data += self._fp.read(delta)
> -+ cursize += delta
> -+
> - def _read_ints(self, n, size):
> -- data = self._fp.read(size * n)
> -+ data = self._read(size * n)
> - if size in _BINARY_FORMAT:
> - return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data)
> - else:
> -- if not size or len(data) != size * n:
> -+ if not size:
> - raise InvalidFileException()
> - return tuple(int.from_bytes(data[i: i + size], 'big')
> - for i in range(0, size * n, size))
> -@@ -561,22 +576,16 @@ class _BinaryPlistParser:
> -
> - elif tokenH == 0x40: # data
> - s = self._get_size(tokenL)
> -- result = self._fp.read(s)
> -- if len(result) != s:
> -- raise InvalidFileException()
> -+ result = self._read(s)
> -
> - elif tokenH == 0x50: # ascii string
> - s = self._get_size(tokenL)
> -- data = self._fp.read(s)
> -- if len(data) != s:
> -- raise InvalidFileException()
> -+ data = self._read(s)
> - result = data.decode('ascii')
> -
> - elif tokenH == 0x60: # unicode string
> - s = self._get_size(tokenL) * 2
> -- data = self._fp.read(s)
> -- if len(data) != s:
> -- raise InvalidFileException()
> -+ data = self._read(s)
> - result = data.decode('utf-16be')
> -
> - elif tokenH == 0x80: # UID
> -diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py
> -index fa46050658..229a5a242e 100644
> ---- a/Lib/test/test_plistlib.py
> -+++ b/Lib/test/test_plistlib.py
> -@@ -838,8 +838,7 @@ class TestPlistlib(unittest.TestCase):
> -
> - class TestBinaryPlistlib(unittest.TestCase):
> -
> -- @staticmethod
> -- def decode(*objects, offset_size=1, ref_size=1):
> -+ def build(self, *objects, offset_size=1, ref_size=1):
> - data = [b'bplist00']
> - offset = 8
> - offsets = []
> -@@ -851,7 +850,11 @@ class TestBinaryPlistlib(unittest.TestCase):
> - len(objects), 0, offset)
> - data.extend(offsets)
> - data.append(tail)
> -- return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY)
> -+ return b''.join(data)
> -+
> -+ def decode(self, *objects, offset_size=1, ref_size=1):
> -+ data = self.build(*objects, offset_size=offset_size, ref_size=ref_size)
> -+ return plistlib.loads(data, fmt=plistlib.FMT_BINARY)
> -
> - def test_nonstandard_refs_size(self):
> - # Issue #21538: Refs and offsets are 24-bit integers
> -@@ -959,6 +962,34 @@ class TestBinaryPlistlib(unittest.TestCase):
> - with self.assertRaises(plistlib.InvalidFileException):
> - plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
> -
> -+ def test_truncated_large_data(self):
> -+ self.addCleanup(os_helper.unlink, os_helper.TESTFN)
> -+ def check(data):
> -+ with open(os_helper.TESTFN, 'wb') as f:
> -+ f.write(data)
> -+ # buffered file
> -+ with open(os_helper.TESTFN, 'rb') as f:
> -+ with self.assertRaises(plistlib.InvalidFileException):
> -+ plistlib.load(f, fmt=plistlib.FMT_BINARY)
> -+ # unbuffered file
> -+ with open(os_helper.TESTFN, 'rb', buffering=0) as f:
> -+ with self.assertRaises(plistlib.InvalidFileException):
> -+ plistlib.load(f, fmt=plistlib.FMT_BINARY)
> -+ for w in range(20, 64):
> -+ s = 1 << w
> -+ # data
> -+ check(self.build(b'\x4f\x13' + s.to_bytes(8, 'big')))
> -+ # ascii string
> -+ check(self.build(b'\x5f\x13' + s.to_bytes(8, 'big')))
> -+ # unicode string
> -+ check(self.build(b'\x6f\x13' + s.to_bytes(8, 'big')))
> -+ # array
> -+ check(self.build(b'\xaf\x13' + s.to_bytes(8, 'big')))
> -+ # dict
> -+ check(self.build(b'\xdf\x13' + s.to_bytes(8, 'big')))
> -+ # number of objects
> -+ check(b'bplist00' + struct.pack('>6xBBQQQ', 1, 1, s, 0, 8))
> -+
> -
> - class TestKeyedArchive(unittest.TestCase):
> - def test_keyed_archive_data(self):
> -diff --git a/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst b/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst
> -new file mode 100644
> -index 0000000000..04fd8faca4
> ---- /dev/null
> -+++ b/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst
> -@@ -0,0 +1,5 @@
> -+Fix a potential memory denial of service in the :mod:`plistlib` module.
> -+When reading a Plist file received from untrusted source, it could cause
> -+an arbitrary amount of memory to be allocated.
> -+This could have led to symptoms including a :exc:`MemoryError`, swapping, out
> -+of memory (OOM) killed processes or containers, or even system crashes.
> diff --git a/meta/recipes-devtools/python/python3/CVE-2025-15282.patch b/meta/recipes-devtools/python/python3/CVE-2025-15282.patch
> deleted file mode 100644
> index 80ef2fcde8..0000000000
> --- a/meta/recipes-devtools/python/python3/CVE-2025-15282.patch
> +++ /dev/null
> @@ -1,68 +0,0 @@
> -From 34d76b00dabde81a793bd06dd8ecb057838c4b38 Mon Sep 17 00:00:00 2001
> -From: Seth Michael Larson <seth@python.org>
> -Date: Sun, 25 Jan 2026 11:05:15 -0600
> -Subject: [PATCH] [3.10] gh-143925: Reject control characters in data: URL
> - mediatypes (#144115)
> -
> -(cherry picked from commit f25509e78e8be6ea73c811ac2b8c928c28841b9f)
> -(cherry picked from commit 2c9c746077d8119b5bcf5142316992e464594946)
> -
> -Upstream-Status: Backport [https://github.com/python/cpython/commit/34d76b00dabde81a793bd06dd8ecb057838c4b38]
> -CVE: CVE-2025-15282
> -Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
> ----
> - Lib/test/test_urllib.py | 8 ++++++++
> - Lib/urllib/request.py | 5 +++++
> - .../2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst | 1 +
> - 3 files changed, 14 insertions(+)
> - create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst
> -
> -diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py
> -index 82f1d9dc2e7bb3..b08fc8f2b19463 100644
> ---- a/Lib/test/test_urllib.py
> -+++ b/Lib/test/test_urllib.py
> -@@ -11,6 +11,7 @@
> - from test import support
> - from test.support import os_helper
> - from test.support import warnings_helper
> -+from test.support import control_characters_c0
> - import os
> - try:
> - import ssl
> -@@ -683,6 +684,13 @@ def test_invalid_base64_data(self):
> - # missing padding character
> - self.assertRaises(ValueError,urllib.request.urlopen,'data:;base64,Cg=')
> -
> -+ def test_invalid_mediatype(self):
> -+ for c0 in control_characters_c0():
> -+ self.assertRaises(ValueError,urllib.request.urlopen,
> -+ f'data:text/html;{c0},data')
> -+ for c0 in control_characters_c0():
> -+ self.assertRaises(ValueError,urllib.request.urlopen,
> -+ f'data:text/html{c0};base64,ZGF0YQ==')
> -
> - class urlretrieve_FileTests(unittest.TestCase):
> - """Test urllib.urlretrieve() on local files"""
> -diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py
> -index 6edde1f73189b1..c378a86a70cbeb 100644
> ---- a/Lib/urllib/request.py
> -+++ b/Lib/urllib/request.py
> -@@ -1654,6 +1654,11 @@ def data_open(self, req):
> - scheme, data = url.split(":",1)
> - mediatype, data = data.split(",",1)
> -
> -+ # Disallow control characters within mediatype.
> -+ if re.search(r"[\x00-\x1F\x7F]", mediatype):
> -+ raise ValueError(
> -+ "Control characters not allowed in data: mediatype")
> -+
> - # even base64 encoded data URLs might be quoted so unquote in any case:
> - data = unquote_to_bytes(data)
> - if mediatype.endswith(";base64"):
> -diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst
> -new file mode 100644
> -index 00000000000000..46109dfbef3ee7
> ---- /dev/null
> -+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst
> -@@ -0,0 +1 @@
> -+Reject control characters in ``data:`` URL media types.
> diff --git a/meta/recipes-devtools/python/python3/CVE-2025-6075.patch b/meta/recipes-devtools/python/python3/CVE-2025-6075.patch
> deleted file mode 100644
> index eab5a882a0..0000000000
> --- a/meta/recipes-devtools/python/python3/CVE-2025-6075.patch
> +++ /dev/null
> @@ -1,364 +0,0 @@
> -From 892747b4cf0f95ba8beb51c0d0658bfaa381ebca Mon Sep 17 00:00:00 2001
> -From: Łukasz Langa <lukasz@langa.pl>
> -Date: Fri, 31 Oct 2025 17:51:32 +0100
> -Subject: [PATCH] gh-136065: Fix quadratic complexity in os.path.expandvars()
> - (GH-134952) (GH-140851)
> -
> -(cherry picked from commit f029e8db626ddc6e3a3beea4eff511a71aaceb5c)
> -
> -Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
> -
> -CVE: CVE-2025-6075
> -
> -Upstream-Status: Backport [https://github.com/python/cpython/commit/892747b4cf0f95ba8beb51c0d0658bfaa381ebca]
> -
> -Signed-off-by: Praveen Kumar <praveen.kumar@windriver.com>
> ----
> - Lib/ntpath.py | 126 ++++++------------
> - Lib/posixpath.py | 43 +++---
> - Lib/test/test_genericpath.py | 14 ++
> - Lib/test/test_ntpath.py | 20 ++-
> - ...-05-30-22-33-27.gh-issue-136065.bu337o.rst | 1 +
> - 5 files changed, 93 insertions(+), 111 deletions(-)
> - create mode 100644 Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
> -
> -diff --git a/Lib/ntpath.py b/Lib/ntpath.py
> -index 9b0cca4..bd2b4e2 100644
> ---- a/Lib/ntpath.py
> -+++ b/Lib/ntpath.py
> -@@ -374,17 +374,23 @@ def expanduser(path):
> - # XXX With COMMAND.COM you can use any characters in a variable name,
> - # XXX except '^|<>='.
> -
> -+_varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)"
> -+_varsub = None
> -+_varsubb = None
> -+
> - def expandvars(path):
> - """Expand shell variables of the forms $var, ${var} and %var%.
> -
> - Unknown variables are left unchanged."""
> - path = os.fspath(path)
> -+ global _varsub, _varsubb
> - if isinstance(path, bytes):
> - if b'$' not in path and b'%' not in path:
> - return path
> -- import string
> -- varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
> -- quote = b'\''
> -+ if not _varsubb:
> -+ import re
> -+ _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub
> -+ sub = _varsubb
> - percent = b'%'
> - brace = b'{'
> - rbrace = b'}'
> -@@ -393,94 +399,44 @@ def expandvars(path):
> - else:
> - if '$' not in path and '%' not in path:
> - return path
> -- import string
> -- varchars = string.ascii_letters + string.digits + '_-'
> -- quote = '\''
> -+ if not _varsub:
> -+ import re
> -+ _varsub = re.compile(_varpattern, re.ASCII).sub
> -+ sub = _varsub
> - percent = '%'
> - brace = '{'
> - rbrace = '}'
> - dollar = '$'
> - environ = os.environ
> -- res = path[:0]
> -- index = 0
> -- pathlen = len(path)
> -- while index < pathlen:
> -- c = path[index:index+1]
> -- if c == quote: # no expansion within single quotes
> -- path = path[index + 1:]
> -- pathlen = len(path)
> -- try:
> -- index = path.index(c)
> -- res += c + path[:index + 1]
> -- except ValueError:
> -- res += c + path
> -- index = pathlen - 1
> -- elif c == percent: # variable or '%'
> -- if path[index + 1:index + 2] == percent:
> -- res += c
> -- index += 1
> -- else:
> -- path = path[index+1:]
> -- pathlen = len(path)
> -- try:
> -- index = path.index(percent)
> -- except ValueError:
> -- res += percent + path
> -- index = pathlen - 1
> -- else:
> -- var = path[:index]
> -- try:
> -- if environ is None:
> -- value = os.fsencode(os.environ[os.fsdecode(var)])
> -- else:
> -- value = environ[var]
> -- except KeyError:
> -- value = percent + var + percent
> -- res += value
> -- elif c == dollar: # variable or '$$'
> -- if path[index + 1:index + 2] == dollar:
> -- res += c
> -- index += 1
> -- elif path[index + 1:index + 2] == brace:
> -- path = path[index+2:]
> -- pathlen = len(path)
> -- try:
> -- index = path.index(rbrace)
> -- except ValueError:
> -- res += dollar + brace + path
> -- index = pathlen - 1
> -- else:
> -- var = path[:index]
> -- try:
> -- if environ is None:
> -- value = os.fsencode(os.environ[os.fsdecode(var)])
> -- else:
> -- value = environ[var]
> -- except KeyError:
> -- value = dollar + brace + var + rbrace
> -- res += value
> -- else:
> -- var = path[:0]
> -- index += 1
> -- c = path[index:index + 1]
> -- while c and c in varchars:
> -- var += c
> -- index += 1
> -- c = path[index:index + 1]
> -- try:
> -- if environ is None:
> -- value = os.fsencode(os.environ[os.fsdecode(var)])
> -- else:
> -- value = environ[var]
> -- except KeyError:
> -- value = dollar + var
> -- res += value
> -- if c:
> -- index -= 1
> -+
> -+ def repl(m):
> -+ lastindex = m.lastindex
> -+ if lastindex is None:
> -+ return m[0]
> -+ name = m[lastindex]
> -+ if lastindex == 1:
> -+ if name == percent:
> -+ return name
> -+ if not name.endswith(percent):
> -+ return m[0]
> -+ name = name[:-1]
> - else:
> -- res += c
> -- index += 1
> -- return res
> -+ if name == dollar:
> -+ return name
> -+ if name.startswith(brace):
> -+ if not name.endswith(rbrace):
> -+ return m[0]
> -+ name = name[1:-1]
> -+
> -+ try:
> -+ if environ is None:
> -+ return os.fsencode(os.environ[os.fsdecode(name)])
> -+ else:
> -+ return environ[name]
> -+ except KeyError:
> -+ return m[0]
> -+
> -+ return sub(repl, path)
> -
> -
> - # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
> -diff --git a/Lib/posixpath.py b/Lib/posixpath.py
> -index b8dd563..75020ee 100644
> ---- a/Lib/posixpath.py
> -+++ b/Lib/posixpath.py
> -@@ -279,42 +279,41 @@ def expanduser(path):
> - # This expands the forms $variable and ${variable} only.
> - # Non-existent variables are left unchanged.
> -
> --_varprog = None
> --_varprogb = None
> -+_varpattern = r'\$(\w+|\{[^}]*\}?)'
> -+_varsub = None
> -+_varsubb = None
> -
> - def expandvars(path):
> - """Expand shell variables of form $var and ${var}. Unknown variables
> - are left unchanged."""
> - path = os.fspath(path)
> -- global _varprog, _varprogb
> -+ global _varsub, _varsubb
> - if isinstance(path, bytes):
> - if b'$' not in path:
> - return path
> -- if not _varprogb:
> -+ if not _varsubb:
> - import re
> -- _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
> -- search = _varprogb.search
> -+ _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub
> -+ sub = _varsubb
> - start = b'{'
> - end = b'}'
> - environ = getattr(os, 'environb', None)
> - else:
> - if '$' not in path:
> - return path
> -- if not _varprog:
> -+ if not _varsub:
> - import re
> -- _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
> -- search = _varprog.search
> -+ _varsub = re.compile(_varpattern, re.ASCII).sub
> -+ sub = _varsub
> - start = '{'
> - end = '}'
> - environ = os.environ
> -- i = 0
> -- while True:
> -- m = search(path, i)
> -- if not m:
> -- break
> -- i, j = m.span(0)
> -- name = m.group(1)
> -- if name.startswith(start) and name.endswith(end):
> -+
> -+ def repl(m):
> -+ name = m[1]
> -+ if name.startswith(start):
> -+ if not name.endswith(end):
> -+ return m[0]
> - name = name[1:-1]
> - try:
> - if environ is None:
> -@@ -322,13 +321,11 @@ def expandvars(path):
> - else:
> - value = environ[name]
> - except KeyError:
> -- i = j
> -+ return m[0]
> - else:
> -- tail = path[j:]
> -- path = path[:i] + value
> -- i = len(path)
> -- path += tail
> -- return path
> -+ return value
> -+
> -+ return sub(repl, path)
> -
> -
> - # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
> -diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py
> -index 1ff7f75..b0a1326 100644
> ---- a/Lib/test/test_genericpath.py
> -+++ b/Lib/test/test_genericpath.py
> -@@ -7,6 +7,7 @@ import os
> - import sys
> - import unittest
> - import warnings
> -+from test import support
> - from test.support import os_helper
> - from test.support import warnings_helper
> - from test.support.script_helper import assert_python_ok
> -@@ -430,6 +431,19 @@ class CommonTest(GenericTest):
> - os.fsencode('$bar%s bar' % nonascii))
> - check(b'$spam}bar', os.fsencode('%s}bar' % nonascii))
> -
> -+ @support.requires_resource('cpu')
> -+ def test_expandvars_large(self):
> -+ expandvars = self.pathmodule.expandvars
> -+ with os_helper.EnvironmentVarGuard() as env:
> -+ env.clear()
> -+ env["A"] = "B"
> -+ n = 100_000
> -+ self.assertEqual(expandvars('$A'*n), 'B'*n)
> -+ self.assertEqual(expandvars('${A}'*n), 'B'*n)
> -+ self.assertEqual(expandvars('$A!'*n), 'B!'*n)
> -+ self.assertEqual(expandvars('${A}A'*n), 'BA'*n)
> -+ self.assertEqual(expandvars('${'*10*n), '${'*10*n)
> -+
> - def test_abspath(self):
> - self.assertIn("foo", self.pathmodule.abspath("foo"))
> - with warnings.catch_warnings():
> -diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
> -index f790f77..161e57d 100644
> ---- a/Lib/test/test_ntpath.py
> -+++ b/Lib/test/test_ntpath.py
> -@@ -5,8 +5,8 @@ import sys
> - import unittest
> - import warnings
> - from ntpath import ALLOW_MISSING
> -+from test import support
> - from test.support import os_helper
> --from test.support import TestFailed
> - from test.support.os_helper import FakePath
> - from test import test_genericpath
> - from tempfile import TemporaryFile
> -@@ -56,7 +56,7 @@ def tester(fn, wantResult):
> - fn = fn.replace("\\", "\\\\")
> - gotResult = eval(fn)
> - if wantResult != gotResult and _norm(wantResult) != _norm(gotResult):
> -- raise TestFailed("%s should return: %s but returned: %s" \
> -+ raise support.TestFailed("%s should return: %s but returned: %s" \
> - %(str(fn), str(wantResult), str(gotResult)))
> -
> - # then with bytes
> -@@ -72,7 +72,7 @@ def tester(fn, wantResult):
> - warnings.simplefilter("ignore", DeprecationWarning)
> - gotResult = eval(fn)
> - if _norm(wantResult) != _norm(gotResult):
> -- raise TestFailed("%s should return: %s but returned: %s" \
> -+ raise support.TestFailed("%s should return: %s but returned: %s" \
> - %(str(fn), str(wantResult), repr(gotResult)))
> -
> -
> -@@ -689,6 +689,19 @@ class TestNtpath(NtpathTestCase):
> - check('%spam%bar', '%sbar' % nonascii)
> - check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
> -
> -+ @support.requires_resource('cpu')
> -+ def test_expandvars_large(self):
> -+ expandvars = ntpath.expandvars
> -+ with os_helper.EnvironmentVarGuard() as env:
> -+ env.clear()
> -+ env["A"] = "B"
> -+ n = 100_000
> -+ self.assertEqual(expandvars('%A%'*n), 'B'*n)
> -+ self.assertEqual(expandvars('%A%A'*n), 'BA'*n)
> -+ self.assertEqual(expandvars("''"*n + '%%'), "''"*n + '%')
> -+ self.assertEqual(expandvars("%%"*n), "%"*n)
> -+ self.assertEqual(expandvars("$$"*n), "$"*n)
> -+
> - def test_expanduser(self):
> - tester('ntpath.expanduser("test")', 'test')
> -
> -@@ -923,6 +936,7 @@ class TestNtpath(NtpathTestCase):
> - self.assertIsInstance(b_final_path, bytes)
> - self.assertGreater(len(b_final_path), 0)
> -
> -+
> - class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
> - pathmodule = ntpath
> - attributes = ['relpath']
> -diff --git a/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst b/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
> -new file mode 100644
> -index 0000000..1d152bb
> ---- /dev/null
> -+++ b/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst
> -@@ -0,0 +1 @@
> -+Fix quadratic complexity in :func:`os.path.expandvars`.
> ---
> -2.40.0
> diff --git a/meta/recipes-devtools/python/python3_3.10.19.bb b/meta/recipes-devtools/python/python3_3.10.20.bb
> similarity index 98%
> rename from meta/recipes-devtools/python/python3_3.10.19.bb
> rename to meta/recipes-devtools/python/python3_3.10.20.bb
> index e2a0ae9fe7..88a57971b9 100644
> --- a/meta/recipes-devtools/python/python3_3.10.19.bb
> +++ b/meta/recipes-devtools/python/python3_3.10.20.bb
> @@ -37,11 +37,6 @@ SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.xz \
> file://0001-Avoid-shebang-overflow-on-python-config.py.patch \
> file://0001-test_storlines-skip-due-to-load-variability.patch \
> file://0001-gh-107811-tarfile-treat-overflow-in-UID-GID-as-failu.patch \
> - file://CVE-2025-6075.patch \
> - file://CVE-2025-13836.patch \
> - file://CVE-2025-13837.patch \
> - file://CVE-2025-12084.patch \
> - file://CVE-2025-15282.patch \
> "
>
> SRC_URI:append:class-native = " \
> @@ -50,7 +45,7 @@ SRC_URI:append:class-native = " \
> file://12-distutils-prefix-is-inside-staging-area.patch \
> file://0001-Don-t-search-system-for-headers-libraries.patch \
> "
> -SRC_URI[sha256sum] = "c8f4a596572201d81dd7df91f70e177e19a70f1d489968b54b5fbbf29a97c076"
> +SRC_URI[sha256sum] = "de6517421601e39a9a3bc3e1bc4c7b2f239297423ee05e282598c83ec0647505"
>
> # exclude pre-releases for both python 2.x and 3.x
> UPSTREAM_CHECK_REGEX = "[Pp]ython-(?P<pver>\d+(\.\d+)+).tar"
--
Yoann Congal
Smile ECS
prev parent reply other threads:[~2026-04-07 6:54 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-06 13:46 [OE-core][kirkstone][PATCH] python3: upgrade 3.10.19 -> 3.10.20 vanusuri
2026-04-07 6:54 ` Yoann Congal [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DHMQ4866I1F6.3ESGDXTBOPBT4@smile.fr \
--to=yoann.congal@smile.fr \
--cc=openembedded-core@lists.openembedded.org \
--cc=vanusuri@mvista.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox