mirror of
https://git.yoctoproject.org/poky
synced 2026-01-29 21:08:42 +01:00
python3: fix CVE-2025-13836
Upstream-Status: Backport from 289f29b0fe
(From OE-Core rev: d3bcb5ded27003612ad591764f648e83e91c27ca)
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
Signed-off-by: Yoann Congal <yoann.congal@smile.fr>
Signed-off-by: Paul Barker <paul@pbarker.dev>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
committed by
Richard Purdie
parent
51201f6c54
commit
cfb6825c35
163
meta/recipes-devtools/python/python3/CVE-2025-13836.patch
Normal file
163
meta/recipes-devtools/python/python3/CVE-2025-13836.patch
Normal file
@@ -0,0 +1,163 @@
|
||||
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
|
||||
|
||||
@@ -38,6 +38,7 @@ SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.xz \
|
||||
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 \
|
||||
"
|
||||
|
||||
SRC_URI:append:class-native = " \
|
||||
|
||||
Reference in New Issue
Block a user