File CVE-2025-13836-http-resp-cont-len.patch of Package python.43089

From b3a7998115e195c40e00cfa662bcaa899d937c05 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Mon, 1 Dec 2025 17:26:07 +0200
Subject: [PATCH] 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)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
---

---
 Lib/http/client.py                                                       |   32 +++-
 Lib/socket.py                                                            |   66 +++++++++
 Lib/test/test_httplib.py                                                 |   70 +++++++++-
 Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst |    5
 Lib/socket.py                                                            |   89 ++++++++++
 Lib/test/test_httplib.py                                                 |   27 +++
 Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst |    5 
 3 files changed, 121 insertions(+)

Index: Python-2.7.18/Lib/socket.py
===================================================================
--- Python-2.7.18.orig/Lib/socket.py	2020-04-19 23:13:39.000000000 +0200
+++ Python-2.7.18/Lib/socket.py	2026-03-07 22:54:49.146231203 +0100
@@ -26,6 +26,7 @@
 socket.setdefaulttimeout() -- set the default timeout value
 create_connection() -- connects to an address, with an optional timeout and
                        optional source address.
+create_server() -- create a TCP socket and bind it to a specified address.
 
  [*] not available on all platforms!
 
@@ -96,6 +97,8 @@
     errno = None
 EBADF = getattr(errno, 'EBADF', 9)
 EINTR = getattr(errno, 'EINTR', 4)
+EAGAIN = getattr(errno, 'EAGAIN', 11)
+EWOULDBLOCK = getattr(errno, 'EWOULDBLOCK', 11)
 
 __all__ = ["getfqdn", "create_connection"]
 __all__.extend(os._get_exports_list(_socket))
@@ -538,6 +541,28 @@
 
 _GLOBAL_DEFAULT_TIMEOUT = object()
 
+def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
+    """Resolve host and port into list of address info entries.
+
+    Translate the host/port argument into a sequence of 5-tuples that contain
+    all the necessary arguments for creating a socket connected to that service.
+    host is a domain name, a string representation of an IPv4/v6 address or
+    None. port is a string service name such as 'http', a numeric port number or
+    None. By passing None as the value of host and port, you can pass NULL to
+    the underlying C API.
+
+    The family, type and proto arguments can be optionally specified in order to
+    narrow the list of addresses returned. Passing zero as a value for each of
+    these arguments selects the full range of results.
+    """
+    # We override this function since we want to translate the numeric family
+    # and socket type values to enum constants.
+    addrlist = []
+    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
+        addrlist.append(res)
+    return addrlist
+
+
 def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
                       source_address=None):
     """Connect to *address* and return the socket object.
@@ -575,3 +600,67 @@
         raise err
     else:
         raise error("getaddrinfo returns an empty list")
+
+def create_server(address, family=None, backlog=None, reuse_port=False):
+    """Convenience function which creates a SOCK_STREAM type socket
+    bound to *address* (a 2-tuple (host, port)) and return the socket
+    object.
+
+    *family* should be either AF_INET or AF_INET6.
+    *backlog* is the queue size passed to socket.listen().
+    *reuse_port* dictates whether to use the SO_REUSEPORT socket option.
+    *dualstack_ipv6*: if true and the platform supports it, it will
+    create an AF_INET6 socket able to accept both IPv4 or IPv6
+    connections. When false it will explicitly disable this option on
+    platforms that enable it by default (e.g. Linux).
+
+    >>> with create_server(('', 8000)) as server:
+    ...     while True:
+    ...         conn, addr = server.accept()
+    ...         # handle new connection
+    """
+    if family is None:
+        family = getattr(socket, "AF_UNSPEC", 0)
+
+    host, port = address
+    err = None
+
+    # AI_PASSIVE tells getaddrinfo we intend to bind() to this address
+    try:
+        addr_infos = getaddrinfo(
+            host, port, family, SOCK_STREAM, 0, AI_PASSIVE
+        )
+    except error as e:
+        raise OSError("getaddrinfo failed for {}: {}".format(address,e))
+
+    for res in addr_infos:
+        af, socktype, proto, canonname, sa = res
+        sock = None
+        try:
+            sock = socket(af, socktype, proto)
+
+            # Standard server socket option
+            sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
+
+            if reuse_port:
+                # Fix 2: Handle platforms/builds where SO_REUSEPORT is missing
+                if hasattr(socket, "SO_REUSEPORT"):
+                    sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
+                else:
+                    # Optional: log a warning or ignore if unavailable
+                    pass
+
+            sock.bind(sa)
+            sock.listen(backlog if backlog is not None else SOMAXCONN)
+
+            return sock  # Success!
+
+        except OSError as e:
+            err = e
+            if sock is not None:
+                sock.close()
+
+    # If we get here, no address worked
+    if err is not None:
+        raise err
+    raise OSError("Could not create server on {}".format(address))
Index: Python-2.7.18/Lib/test/test_httplib.py
===================================================================
--- Python-2.7.18.orig/Lib/test/test_httplib.py	2026-03-07 22:54:48.823268651 +0100
+++ Python-2.7.18/Lib/test/test_httplib.py	2026-03-07 23:21:35.471100829 +0100
@@ -785,6 +785,32 @@
         self.assertEqual(self.conn._conn.host, HOST)
         self.assertEqual(self.conn._conn.port, self.port)
 
+    def test_large_content_length(self):
+        # Test that reading large content works correctly (chunked reading)
+        # This tests the CVE-2025-13836 fix - large Content-Length is handled in chunks
+        # Use smaller sizes to avoid memory issues in test environment
+        for size in [1000, 5000, 10000]:
+            conn = httplib.HTTPConnection('example.com')
+            sock = FakeSocket("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s" %
+                              (size, 'A' * size))
+            conn.sock = sock
+            conn.request('GET', '/')
+            response = conn.getresponse()
+            data = response.read()
+            self.assertEqual(len(data), size)
+            conn.close()
+
+    def test_large_content_length_truncated(self):
+        # Test that IncompleteRead is raised when server sends less than Content-Length
+        # Use a smaller size for testing
+        conn = httplib.HTTPConnection('example.com')
+        sock = FakeSocket("HTTP/1.1 200 OK\r\nContent-Length: 1000\r\n\r\nShort")
+        conn.sock = sock
+        conn.request('GET', '/')
+        response = conn.getresponse()
+        self.assertRaises(httplib.IncompleteRead, response.read)
+        conn.close()
+
 
 class TimeoutTest(TestCase):
     PORT = None
Index: Python-2.7.18/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ Python-2.7.18/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst	2026-03-07 22:54:49.147179222 +0100
@@ -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.
openSUSE Build Service is sponsored by