Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python39 for openSUSE:Factory 
checked in at 2025-12-24 13:15:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python39 (Old)
 and      /work/SRC/openSUSE:Factory/.python39.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python39"

Wed Dec 24 13:15:58 2025 rev:84 rq:1324332 version:3.9.25

Changes:
--------
--- /work/SRC/openSUSE:Factory/python39/python39.changes        2025-12-12 
21:44:03.744134237 +0100
+++ /work/SRC/openSUSE:Factory/.python39.new.1928/python39.changes      
2025-12-24 13:17:07.036455170 +0100
@@ -1,0 +2,14 @@
+Thu Dec 18 10:33:44 UTC 2025 - Matej Cepl <[email protected]>
+
+- Add CVE-2025-13836-http-resp-cont-len.patch (bsc#1254400,
+  CVE-2025-13836) to prevent reading an HTTP response from
+  a server, if no read amount is specified, with using
+  Content-Length per default as the length.
+- Add CVE-2025-12084-minidom-quad-search.patch prevent quadratic
+  behavior in node ID cache clearing (CVE-2025-12084,
+  bsc#1254997).
+- Add CVE-2025-13837-plistlib-mailicious-length.patch protect
+  against OOM when loading malicious content (CVE-2025-13837,
+  bsc#1254401).
+
+-------------------------------------------------------------------

New:
----
  CVE-2025-12084-minidom-quad-search.patch
  CVE-2025-13836-http-resp-cont-len.patch
  CVE-2025-13837-plistlib-mailicious-length.patch

----------(New B)----------
  New:  Content-Length per default as the length.
- Add CVE-2025-12084-minidom-quad-search.patch prevent quadratic
  behavior in node ID cache clearing (CVE-2025-12084,
  New:
- Add CVE-2025-13836-http-resp-cont-len.patch (bsc#1254400,
  CVE-2025-13836) to prevent reading an HTTP response from
  New:  bsc#1254997).
- Add CVE-2025-13837-plistlib-mailicious-length.patch protect
  against OOM when loading malicious content (CVE-2025-13837,
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python39.spec ++++++
--- /var/tmp/diff_new_pack.fwjPs6/_old  2025-12-24 13:17:08.964534821 +0100
+++ /var/tmp/diff_new_pack.fwjPs6/_new  2025-12-24 13:17:08.968534986 +0100
@@ -194,6 +194,15 @@
 Patch51:        sphinx-802.patch
 # PATCH-FIX-OPENSUSE gh139257-Support-docutils-0.22.patch 
gh#python/cpython#139257 [email protected]
 Patch52:        gh139257-Support-docutils-0.22.patch
+# PATCH-FIX-UPSTREAM CVE-2025-13836-http-resp-cont-len.patch bsc#1254400 
[email protected]
+# Avoid loading possibly compromised length of HTTP response
+Patch53:         CVE-2025-13836-http-resp-cont-len.patch
+# PATCH-FIX-UPSTREAM CVE-2025-12084-minidom-quad-search.patch bsc#1254997 
[email protected]
+# prevent quadratic behavior in node ID cache clearing 
+Patch54:        CVE-2025-12084-minidom-quad-search.patch
+# PATCH-FIX-UPSTREAM CVE-2025-13837-plistlib-mailicious-length.patch 
bsc#1254401 [email protected]
+# protect against OOM when loading malicious content
+Patch55:        CVE-2025-13837-plistlib-mailicious-length.patch 
 BuildRequires:  autoconf-archive
 BuildRequires:  automake
 BuildRequires:  fdupes
@@ -466,6 +475,9 @@
 %patch -p1 -P 50
 %patch -p1 -P 51
 %patch -p1 -P 52
+%patch -p1 -P 53
+%patch -p1 -P 54
+%patch -p1 -P 55
 
 # drop Autoconf version requirement
 sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac

++++++ CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch ++++++
--- /var/tmp/diff_new_pack.fwjPs6/_old  2025-12-24 13:17:09.020537134 +0100
+++ /var/tmp/diff_new_pack.fwjPs6/_new  2025-12-24 13:17:09.024537300 +0100
@@ -44,10 +44,10 @@
  15 files changed, 77 insertions(+), 873 deletions(-)
  create mode 100644 
Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst
 
-Index: Python-3.9.24/Doc/using/unix.rst
+Index: Python-3.9.25/Doc/using/unix.rst
 ===================================================================
---- Python-3.9.24.orig/Doc/using/unix.rst      2025-11-14 00:54:58.674489238 
+0100
-+++ Python-3.9.24/Doc/using/unix.rst   2025-11-14 00:55:05.521462804 +0100
+--- Python-3.9.25.orig/Doc/using/unix.rst      2025-12-19 23:25:00.457106547 
+0100
++++ Python-3.9.25/Doc/using/unix.rst   2025-12-19 23:25:05.628832227 +0100
 @@ -113,6 +113,7 @@
  |                                               | embedding the interpreter.  
             |
  
+-----------------------------------------------+------------------------------------------+
@@ -56,10 +56,10 @@
  
  Miscellaneous
  =============
-Index: Python-3.9.24/Lib/ssl.py
+Index: Python-3.9.25/Lib/ssl.py
 ===================================================================
---- Python-3.9.24.orig/Lib/ssl.py      2025-11-14 00:54:58.674489238 +0100
-+++ Python-3.9.24/Lib/ssl.py   2025-11-14 00:55:05.522031528 +0100
+--- Python-3.9.25.orig/Lib/ssl.py      2025-12-19 23:25:00.457106547 +0100
++++ Python-3.9.25/Lib/ssl.py   2025-12-19 23:25:05.629281589 +0100
 @@ -912,15 +912,12 @@
          """Return the currently selected NPN protocol as a string, or ``None``
          if a next protocol was not negotiated or if NPN is not supported by 
one
@@ -89,10 +89,10 @@
  
      @_sslcopydoc
      def selected_alpn_protocol(self):
-Index: Python-3.9.24/Lib/test/test_ssl.py
+Index: Python-3.9.25/Lib/test/test_ssl.py
 ===================================================================
---- Python-3.9.24.orig/Lib/test/test_ssl.py    2025-11-14 00:54:58.674489238 
+0100
-+++ Python-3.9.24/Lib/test/test_ssl.py 2025-11-14 00:55:05.522484943 +0100
+--- Python-3.9.25.orig/Lib/test/test_ssl.py    2025-12-19 23:25:00.457106547 
+0100
++++ Python-3.9.25/Lib/test/test_ssl.py 2025-12-19 23:25:05.630031412 +0100
 @@ -39,7 +39,6 @@
  PROTOCOLS = sorted(ssl._PROTOCOL_NAMES)
  HOST = socket_helper.HOST
@@ -332,16 +332,16 @@
          self.assertFalse(stats['session_reused'])
          sess_stat = server_context.session_stats()
          self.assertEqual(sess_stat['accept'], 1)
-Index: 
Python-3.9.24/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst
+Index: 
Python-3.9.25/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst
 ===================================================================
 --- /dev/null  1970-01-01 00:00:00.000000000 +0000
-+++ 
Python-3.9.24/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst   
   2025-11-14 00:55:05.523862509 +0100
++++ 
Python-3.9.25/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst   
   2025-12-19 23:25:05.630820206 +0100
 @@ -0,0 +1 @@
 +Implement :pep:`644`. Python now requires OpenSSL 1.1.1 or newer.
-Index: Python-3.9.24/Modules/Setup
+Index: Python-3.9.25/Modules/Setup
 ===================================================================
---- Python-3.9.24.orig/Modules/Setup   2025-11-14 00:54:58.674489238 +0100
-+++ Python-3.9.24/Modules/Setup        2025-11-14 00:55:05.524260958 +0100
+--- Python-3.9.25.orig/Modules/Setup   2025-12-19 23:25:00.457106547 +0100
++++ Python-3.9.25/Modules/Setup        2025-12-19 23:25:05.631164735 +0100
 @@ -210,11 +210,23 @@
  #_socket socketmodule.c
  
@@ -371,10 +371,10 @@
  
  # The crypt module is now disabled by default because it breaks builds
  # on many systems (where -lcrypt is needed), e.g. Linux (I believe).
-Index: Python-3.9.24/Modules/_hashopenssl.c
+Index: Python-3.9.25/Modules/_hashopenssl.c
 ===================================================================
---- Python-3.9.24.orig/Modules/_hashopenssl.c  2025-11-14 00:54:58.674489238 
+0100
-+++ Python-3.9.24/Modules/_hashopenssl.c       2025-11-14 00:55:05.524484942 
+0100
+--- Python-3.9.25.orig/Modules/_hashopenssl.c  2025-12-19 23:25:00.457106547 
+0100
++++ Python-3.9.25/Modules/_hashopenssl.c       2025-12-19 23:25:05.631562345 
+0100
 @@ -43,51 +43,12 @@
  #  error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL"
  #endif
@@ -557,10 +557,10 @@
 -    return m;
 +    return PyModuleDef_Init(&_hashlibmodule);
  }
-Index: Python-3.9.24/Modules/_ssl.c
+Index: Python-3.9.25/Modules/_ssl.c
 ===================================================================
---- Python-3.9.24.orig/Modules/_ssl.c  2025-11-14 00:54:58.674489238 +0100
-+++ Python-3.9.24/Modules/_ssl.c       2025-11-14 00:55:05.525585095 +0100
+--- Python-3.9.25.orig/Modules/_ssl.c  2025-12-19 23:25:00.457106547 +0100
++++ Python-3.9.25/Modules/_ssl.c       2025-12-19 23:25:05.632257411 +0100
 @@ -29,9 +29,9 @@
  #define _PySSL_FIX_ERRNO
  
@@ -1552,10 +1552,10 @@
  
  #if defined(SSL2_VERSION) && !defined(OPENSSL_NO_SSL2)
      addbool(m, "HAS_SSLv2", 1);
-Index: Python-3.9.24/Modules/_ssl/debughelpers.c
+Index: Python-3.9.25/Modules/_ssl/debughelpers.c
 ===================================================================
---- Python-3.9.24.orig/Modules/_ssl/debughelpers.c     2025-11-14 
00:54:58.674489238 +0100
-+++ Python-3.9.24/Modules/_ssl/debughelpers.c  2025-11-14 00:55:05.526651095 
+0100
+--- Python-3.9.25.orig/Modules/_ssl/debughelpers.c     2025-12-19 
23:25:00.457106547 +0100
++++ Python-3.9.25/Modules/_ssl/debughelpers.c  2025-12-19 23:25:05.633150689 
+0100
 @@ -114,8 +114,6 @@
      return 0;
  }
@@ -1571,10 +1571,10 @@
  }
 -
 -#endif
-Index: Python-3.9.24/Modules/clinic/_hashopenssl.c.h
+Index: Python-3.9.25/Modules/clinic/_hashopenssl.c.h
 ===================================================================
---- Python-3.9.24.orig/Modules/clinic/_hashopenssl.c.h 2025-11-14 
00:54:58.674489238 +0100
-+++ Python-3.9.24/Modules/clinic/_hashopenssl.c.h      2025-11-14 
00:55:05.527005334 +0100
+--- Python-3.9.25.orig/Modules/clinic/_hashopenssl.c.h 2025-12-19 
23:25:00.457106547 +0100
++++ Python-3.9.25/Modules/clinic/_hashopenssl.c.h      2025-12-19 
23:25:05.633452755 +0100
 @@ -965,7 +965,7 @@
      return return_value;
  }
@@ -1619,10 +1619,10 @@
 -    #define _HASHLIB_GET_FIPS_MODE_METHODDEF
 -#endif /* !defined(_HASHLIB_GET_FIPS_MODE_METHODDEF) */
  /*[clinic end generated code: output=b6b280e46bf0b139 
input=a9049054013a1b77]*/
-Index: Python-3.9.24/Modules/clinic/_ssl.c.h
+Index: Python-3.9.25/Modules/clinic/_ssl.c.h
 ===================================================================
---- Python-3.9.24.orig/Modules/clinic/_ssl.c.h 2025-11-14 00:54:58.674489238 
+0100
-+++ Python-3.9.24/Modules/clinic/_ssl.c.h      2025-11-14 00:55:05.527465105 
+0100
+--- Python-3.9.25.orig/Modules/clinic/_ssl.c.h 2025-12-19 23:25:00.457106547 
+0100
++++ Python-3.9.25/Modules/clinic/_ssl.c.h      2025-12-19 23:25:05.633884238 
+0100
 @@ -139,29 +139,6 @@
      return _ssl__SSLSocket_version_impl(self);
  }
@@ -1757,11 +1757,11 @@
  #ifndef _SSL_RAND_EGD_METHODDEF
      #define _SSL_RAND_EGD_METHODDEF
  #endif /* !defined(_SSL_RAND_EGD_METHODDEF) */
-Index: Python-3.9.24/Tools/ssl/multissltests.py
+Index: Python-3.9.25/Tools/ssl/multissltests.py
 ===================================================================
---- Python-3.9.24.orig/Tools/ssl/multissltests.py      2025-11-14 
00:54:58.674489238 +0100
-+++ Python-3.9.24/Tools/ssl/multissltests.py   2025-11-14 00:55:05.527928158 
+0100
-@@ -43,8 +43,6 @@
+--- Python-3.9.25.orig/Tools/ssl/multissltests.py      2025-12-19 
23:25:00.457106547 +0100
++++ Python-3.9.25/Tools/ssl/multissltests.py   2025-12-19 23:25:05.634262920 
+0100
+@@ -44,8 +44,6 @@
  log = logging.getLogger("multissl")
  
  OPENSSL_OLD_VERSIONS = [
@@ -1770,7 +1770,7 @@
  ]
  
  OPENSSL_RECENT_VERSIONS = [
-@@ -53,11 +51,9 @@
+@@ -54,11 +52,9 @@
  ]
  
  LIBRESSL_OLD_VERSIONS = [
@@ -1782,10 +1782,10 @@
  ]
  
  # store files in ../multissl
-Index: Python-3.9.24/configure
+Index: Python-3.9.25/configure
 ===================================================================
---- Python-3.9.24.orig/configure       2025-11-14 00:54:58.674489238 +0100
-+++ Python-3.9.24/configure    2025-11-14 00:55:05.530484938 +0100
+--- Python-3.9.25.orig/configure       2025-12-19 23:25:00.457106547 +0100
++++ Python-3.9.25/configure    2025-12-19 23:25:05.636084606 +0100
 @@ -88,6 +88,13 @@
  # splitting by setting IFS to empty value.)
  IFS=" ""      $as_nl"
@@ -1813,10 +1813,10 @@
      echo "" >&6
  fi
 -
-Index: Python-3.9.24/configure.ac
+Index: Python-3.9.25/configure.ac
 ===================================================================
---- Python-3.9.24.orig/configure.ac    2025-11-14 00:54:58.674489238 +0100
-+++ Python-3.9.24/configure.ac 2025-11-14 00:55:05.531555268 +0100
+--- Python-3.9.25.orig/configure.ac    2025-12-19 23:25:00.457106547 +0100
++++ Python-3.9.25/configure.ac 2025-12-19 23:25:05.637165408 +0100
 @@ -5756,42 +5756,6 @@
  # Check for usable OpenSSL
  AX_CHECK_OPENSSL([have_openssl=yes],[have_openssl=no])
@@ -1860,10 +1860,10 @@
  # ssl module default cipher suite string
  AH_TEMPLATE(PY_SSL_DEFAULT_CIPHERS,
    [Default cipher suites list for ssl module.
-Index: Python-3.9.24/pyconfig.h.in
+Index: Python-3.9.25/pyconfig.h.in
 ===================================================================
---- Python-3.9.24.orig/pyconfig.h.in   2025-11-14 00:54:58.674489238 +0100
-+++ Python-3.9.24/pyconfig.h.in        2025-11-14 00:55:05.532315919 +0100
+--- Python-3.9.25.orig/pyconfig.h.in   2025-12-19 23:25:00.457106547 +0100
++++ Python-3.9.25/pyconfig.h.in        2025-12-19 23:25:05.638051492 +0100
 @@ -1351,9 +1351,6 @@
  /* Define to 1 if you have the `writev' function. */
  #undef HAVE_WRITEV
@@ -1874,10 +1874,10 @@
  /* Define if the zlib library has inflateCopy */
  #undef HAVE_ZLIB_COPY
  
-Index: Python-3.9.24/setup.py
+Index: Python-3.9.25/setup.py
 ===================================================================
---- Python-3.9.24.orig/setup.py        2025-11-14 00:54:58.674489238 +0100
-+++ Python-3.9.24/setup.py     2025-11-14 00:55:05.532484937 +0100
+--- Python-3.9.25.orig/setup.py        2025-12-19 23:25:00.457106547 +0100
++++ Python-3.9.25/setup.py     2025-12-19 23:25:05.638647942 +0100
 @@ -539,10 +539,7 @@
                 for l in (self.missing, self.failed, self.failed_on_import)):
              print()

++++++ CVE-2025-12084-minidom-quad-search.patch ++++++
>From f4eb9ab014545b521fb261b80adfa6d138e7e092 Mon Sep 17 00:00:00 2001
From: Seth Michael Larson <[email protected]>
Date: Wed, 3 Dec 2025 01:16:37 -0600
Subject: [PATCH] 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 <[email protected]>

* Add news fragment

---------
(cherry picked from commit 08d8e18ad81cd45bc4a27d6da478b51ea49486e4)

Co-authored-by: Seth Michael Larson <[email protected]>
Co-authored-by: Jacob Walls <[email protected]>
---
 Lib/test/test_minidom.py                                                 |   
18 ++++++++++
 Lib/xml/dom/minidom.py                                                   |    
9 -----
 Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst |    
1 
 3 files changed, 20 insertions(+), 8 deletions(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst

Index: Python-3.9.25/Lib/test/test_minidom.py
===================================================================
--- Python-3.9.25.orig/Lib/test/test_minidom.py 2025-12-19 23:24:47.176384491 
+0100
+++ Python-3.9.25/Lib/test/test_minidom.py      2025-12-19 23:27:27.634483015 
+0100
@@ -2,6 +2,7 @@
 
 import copy
 import pickle
+import time
 import io
 from test import support
 import unittest
@@ -162,6 +163,23 @@
         self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
         dom.unlink()
 
+    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.time()
+        for child in children:
+            element.appendChild(child)
+            element = child
+        end = time.time()
+
+        # This example used to take at least 30 seconds.
+        self.assertLess(end - start, 1)
+
     def testAppendChildFragment(self):
         dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
         dom.documentElement.appendChild(frag)
Index: Python-3.9.25/Lib/xml/dom/minidom.py
===================================================================
--- Python-3.9.25.orig/Lib/xml/dom/minidom.py   2025-10-31 19:40:52.000000000 
+0100
+++ Python-3.9.25/Lib/xml/dom/minidom.py        2025-12-19 23:27:27.635128214 
+0100
@@ -292,13 +292,6 @@
     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."
@@ -1537,7 +1530,7 @@
     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
 
Index: 
Python-3.9.25/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.9.25/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
      2025-12-19 23:27:27.635403950 +0100
@@ -0,0 +1 @@
+Remove quadratic behavior in ``xml.minidom`` node ID cache clearing.

++++++ CVE-2025-13836-http-resp-cont-len.patch ++++++
>From b3a7998115e195c40e00cfa662bcaa899d937c05 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <[email protected]>
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 <[email protected]>
---
 Lib/http/client.py                                                       |   
32 +++-
 Lib/test/test_httplib.py                                                 |   
66 ++++++++++
 Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst |    
5 
 3 files changed, 95 insertions(+), 8 deletions(-)
 create mode 100644 
Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst

Index: Python-3.9.25/Lib/http/client.py
===================================================================
--- Python-3.9.25.orig/Lib/http/client.py       2025-10-31 19:40:52.000000000 
+0100
+++ Python-3.9.25/Lib/http/client.py    2025-12-19 23:26:40.448421016 +0100
@@ -113,6 +113,11 @@
 _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
@@ -621,14 +626,25 @@
         reading. If the bytes are truly not available (due to EOF), then the
         IncompleteRead exception can be used to detect the problem.
         """
-        s = []
-        while amt > 0:
-            chunk = self.fp.read(min(amt, MAXAMOUNT))
-            if not chunk:
-                raise IncompleteRead(b''.join(s), amt)
-            s.append(chunk)
-            amt -= len(chunk)
-        return b"".join(s)
+        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."""
Index: Python-3.9.25/Lib/test/test_httplib.py
===================================================================
--- Python-3.9.25.orig/Lib/test/test_httplib.py 2025-10-31 19:40:52.000000000 
+0100
+++ Python-3.9.25/Lib/test/test_httplib.py      2025-12-19 23:25:14.713217193 
+0100
@@ -1196,6 +1196,72 @@
         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
Index: 
Python-3.9.25/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-3.9.25/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
      2025-12-19 23:25:14.713527850 +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.

++++++ CVE-2025-13837-plistlib-mailicious-length.patch ++++++
>From e99059d800b741504ef18693803927a0dc062be4 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <[email protected]>
Date: Mon, 1 Dec 2025 17:28:15 +0200
Subject: [PATCH] [3.10] gh-119342: Fix a potential denial of service in
 plistlib (GH-119343)

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)

Co-authored-by: Serhiy Storchaka <[email protected]>
---
 Lib/plistlib.py                                                          |   
31 +++++---
 Lib/test/test_plistlib.py                                                |   
37 +++++++++-
 Misc/NEWS.d/next/Security/2024-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

Index: Python-3.9.25/Lib/plistlib.py
===================================================================
--- Python-3.9.25.orig/Lib/plistlib.py  2025-12-23 23:47:30.450823742 +0100
+++ Python-3.9.25/Lib/plistlib.py       2025-12-23 23:49:03.726727983 +0100
@@ -64,6 +64,9 @@
 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):
@@ -490,12 +493,24 @@
 
         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))
@@ -552,22 +567,16 @@
 
         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
Index: Python-3.9.25/Lib/test/test_plistlib.py
===================================================================
--- Python-3.9.25.orig/Lib/test/test_plistlib.py        2025-12-23 
23:47:31.633839488 +0100
+++ Python-3.9.25/Lib/test/test_plistlib.py     2025-12-23 23:50:05.844028198 
+0100
@@ -837,8 +837,7 @@
 
 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 = []
@@ -850,7 +849,11 @@
                            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
@@ -958,6 +961,34 @@
                 with self.assertRaises(plistlib.InvalidFileException):
                     plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
 
+    def test_truncated_large_data(self):
+        self.addCleanup(support.unlink, support.TESTFN)
+        def check(data):
+            with open(support.TESTFN, 'wb') as f:
+                f.write(data)
+            # buffered file
+            with open(support.TESTFN, 'rb') as f:
+                with self.assertRaises(plistlib.InvalidFileException):
+                    plistlib.load(f, fmt=plistlib.FMT_BINARY)
+            # unbuffered file
+            with open(support.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):
Index: 
Python-3.9.25/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ 
Python-3.9.25/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst
      2025-12-23 23:49:03.727528792 +0100
@@ -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.

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.fwjPs6/_old  2025-12-24 13:17:09.164543083 +0100
+++ /var/tmp/diff_new_pack.fwjPs6/_new  2025-12-24 13:17:09.168543249 +0100
@@ -1,6 +1,6 @@
-mtime: 1765537560
-commit: 793408c576b707274e653d1065a1f1e2c18f08289ac832c651611a9b4b688775
+mtime: 1766530254
+commit: e900ddc04a8f67f1234b6218d542ae9ab312f6a31a98d3fba9867fcca301f192
 url: https://src.opensuse.org/python-interpreters/python39.git
-revision: 793408c576b707274e653d1065a1f1e2c18f08289ac832c651611a9b4b688775
+revision: e900ddc04a8f67f1234b6218d542ae9ab312f6a31a98d3fba9867fcca301f192
 projectscmsync: https://src.opensuse.org/python-interpreters/_ObsPrj
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2025-12-23 23:51:22.000000000 +0100
@@ -0,0 +1,2 @@
+.osc
+python39-*-build/

Reply via email to