diff --git a/CVE-2023-52425-libexpat-2.6.0-backport.patch b/CVE-2023-52425-libexpat-2.6.0-backport.patch new file mode 100644 index 0000000..334b7b8 --- /dev/null +++ b/CVE-2023-52425-libexpat-2.6.0-backport.patch @@ -0,0 +1,57 @@ +--- + Lib/test/test_pyexpat.py | 4 ++++ + Lib/test/test_sax.py | 3 +++ + Lib/test/test_xml_etree.py | 7 +++++++ + 3 files changed, 14 insertions(+) + +--- a/Lib/test/test_pyexpat.py ++++ b/Lib/test/test_pyexpat.py +@@ -766,6 +766,10 @@ class ReparseDeferralTest(unittest.TestC + self.assertEqual(started, ['doc']) + + def test_reparse_deferral_disabled(self): ++ if expat.version_info < (2, 6, 0): ++ self.skipTest(f'Expat {expat.version_info} does not ' ++ 'support reparse deferral') ++ + started = [] + + def start_element(name, _): +--- a/Lib/test/test_sax.py ++++ b/Lib/test/test_sax.py +@@ -1240,6 +1240,9 @@ class ExpatReaderTest(XmlTestBase): + + self.assertEqual(result.getvalue(), start + b"") + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Expat {pyexpat.version_info} does not ' ++ 'support reparse deferral') + def test_flush_reparse_deferral_disabled(self): + result = BytesIO() + xmlgen = XMLGenerator(result) +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -1420,9 +1420,13 @@ class XMLPullParserTest(unittest.TestCas + self.assert_event_tags(parser, [('end', 'root')]) + self.assertIsNone(parser.close()) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Fail with patched version of Expat {pyexpat.version_info}') + def test_simple_xml_chunk_1(self): + self.test_simple_xml(chunk_size=1, flush=True) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Fail with patched version of Expat {pyexpat.version_info}') + def test_simple_xml_chunk_5(self): + self.test_simple_xml(chunk_size=5, flush=True) + +@@ -1648,6 +1652,9 @@ class XMLPullParserTest(unittest.TestCas + + self.assert_event_tags(parser, [('end', 'doc')]) + ++ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ f'Expat {pyexpat.version_info} does not ' ++ 'support reparse deferral') + def test_flush_reparse_deferral_disabled(self): + parser = ET.XMLPullParser(events=('start', 'end')) + diff --git a/CVE-2024-6232-cookies-quad-complex.patch b/CVE-2024-6232-cookies-quad-complex.patch new file mode 100644 index 0000000..8522381 --- /dev/null +++ b/CVE-2024-6232-cookies-quad-complex.patch @@ -0,0 +1,125 @@ +From 15eec9d5076b780463c3dc73afcef688651c5295 Mon Sep 17 00:00:00 2001 +From: Serhiy Storchaka +Date: Sat, 17 Aug 2024 16:30:52 +0300 +Subject: [PATCH] gh-123067: Fix quadratic complexity in parsing "-quoted + cookie values with backslashes (GH-123075) + +This fixes CVE-2024-7592. +(cherry picked from commit 44e458357fca05ca0ae2658d62c8c595b048b5ef) + +Co-authored-by: Serhiy Storchaka +--- + Lib/http/cookies.py | 34 ++------ + Lib/test/test_http_cookies.py | 38 ++++++++++ + Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst | 1 + 3 files changed, 47 insertions(+), 26 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst + +--- a/Lib/http/cookies.py ++++ b/Lib/http/cookies.py +@@ -184,8 +184,13 @@ def _quote(str): + return '"' + str.translate(_Translator) + '"' + + +-_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]") +-_QuotePatt = re.compile(r"[\\].") ++_unquote_sub = re.compile(r'\\(?:([0-3][0-7][0-7])|(.))').sub ++ ++def _unquote_replace(m): ++ if m[1]: ++ return chr(int(m[1], 8)) ++ else: ++ return m[2] + + def _unquote(str): + # If there aren't any doublequotes, +@@ -205,30 +210,7 @@ def _unquote(str): + # \012 --> \n + # \" --> " + # +- i = 0 +- n = len(str) +- res = [] +- while 0 <= i < n: +- o_match = _OctalPatt.search(str, i) +- q_match = _QuotePatt.search(str, i) +- if not o_match and not q_match: # Neither matched +- res.append(str[i:]) +- break +- # else: +- j = k = -1 +- if o_match: +- j = o_match.start(0) +- if q_match: +- k = q_match.start(0) +- if q_match and (not o_match or k < j): # QuotePatt matched +- res.append(str[i:k]) +- res.append(str[k+1]) +- i = k + 2 +- else: # OctalPatt matched +- res.append(str[i:j]) +- res.append(chr(int(str[j+1:j+4], 8))) +- i = j + 4 +- return _nulljoin(res) ++ return _unquote_sub(_unquote_replace, str) + + # The _getdate() routine is used to set the expiration time in the cookie's HTTP + # header. By default, _getdate() returns the current time in the appropriate +--- a/Lib/test/test_http_cookies.py ++++ b/Lib/test/test_http_cookies.py +@@ -5,6 +5,7 @@ from test.support import run_unittest, r + import unittest + from http import cookies + import pickle ++from test import support + + + class CookieTests(unittest.TestCase): +@@ -58,6 +59,43 @@ class CookieTests(unittest.TestCase): + for k, v in sorted(case['dict'].items()): + self.assertEqual(C[k].value, v) + ++ def test_unquote(self): ++ cases = [ ++ (r'a="b=\""', 'b="'), ++ (r'a="b=\\"', 'b=\\'), ++ (r'a="b=\="', 'b=='), ++ (r'a="b=\n"', 'b=n'), ++ (r'a="b=\042"', 'b="'), ++ (r'a="b=\134"', 'b=\\'), ++ (r'a="b=\377"', 'b=\xff'), ++ (r'a="b=\400"', 'b=400'), ++ (r'a="b=\42"', 'b=42'), ++ (r'a="b=\\042"', 'b=\\042'), ++ (r'a="b=\\134"', 'b=\\134'), ++ (r'a="b=\\\""', 'b=\\"'), ++ (r'a="b=\\\042"', 'b=\\"'), ++ (r'a="b=\134\""', 'b=\\"'), ++ (r'a="b=\134\042"', 'b=\\"'), ++ ] ++ for encoded, decoded in cases: ++ with self.subTest(encoded): ++ C = cookies.SimpleCookie() ++ C.load(encoded) ++ self.assertEqual(C['a'].value, decoded) ++ ++ @support.requires_resource('cpu') ++ def test_unquote_large(self): ++ n = 10**6 ++ for encoded in r'\\', r'\134': ++ with self.subTest(encoded): ++ data = 'a="b=' + encoded*n + ';"' ++ C = cookies.SimpleCookie() ++ C.load(data) ++ value = C['a'].value ++ self.assertEqual(value[:3], 'b=\\') ++ self.assertEqual(value[-2:], '\\;') ++ self.assertEqual(len(value), n + 3) ++ + def test_load(self): + C = cookies.SimpleCookie() + C.load('Customer="WILE_E_COYOTE"; Version=1; Path=/acme') +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst +@@ -0,0 +1 @@ ++Fix quadratic complexity in parsing ``"``-quoted cookie values with backslashes by :mod:`http.cookies`. diff --git a/gh120226-fix-sendfile-test-kernel-610.patch b/gh120226-fix-sendfile-test-kernel-610.patch new file mode 100644 index 0000000..79a50dc --- /dev/null +++ b/gh120226-fix-sendfile-test-kernel-610.patch @@ -0,0 +1,35 @@ +From 1b3f6523a5c83323cdc44031b33a1c062e5dc698 Mon Sep 17 00:00:00 2001 +From: Xi Ruoyao +Date: Fri, 7 Jun 2024 23:51:32 +0800 +Subject: [PATCH] gh-120226: Fix + test_sendfile_close_peer_in_the_middle_of_receiving on Linux >= 6.10 + (GH-120227) + +The worst case is that the kernel buffers 17 pages with a page size of 64k. +(cherry picked from commit a7584245661102a5768c643fbd7db8395fd3c90e) + +Co-authored-by: Xi Ruoyao +--- + Lib/test/test_asyncio/test_sendfile.py | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +--- a/Lib/test/test_asyncio/test_sendfile.py ++++ b/Lib/test/test_asyncio/test_sendfile.py +@@ -87,13 +87,10 @@ class MyProto(asyncio.Protocol): + + class SendfileBase: + +- # 256 KiB plus small unaligned to buffer chunk +- # Newer versions of Windows seems to have increased its internal +- # buffer and tries to send as much of the data as it can as it +- # has some form of buffering for this which is less than 256KiB +- # on newer server versions and Windows 11. +- # So DATA should be larger than 256 KiB to make this test reliable. +- DATA = b"x" * (1024 * 256 + 1) ++ # Linux >= 6.10 seems buffering up to 17 pages of data. ++ # So DATA should be large enough to make this test reliable even with a ++ # 64 KiB page configuration. ++ DATA = b"x" * (1024 * 17 * 64 + 1) + # Reduce socket buffer size to test on relative small data sets. + BUF_SIZE = 4 * 1024 # 4 KiB + diff --git a/old-libexpat.patch b/old-libexpat.patch deleted file mode 100644 index 77f45e3..0000000 --- a/old-libexpat.patch +++ /dev/null @@ -1,79 +0,0 @@ ---- - Lib/test/test_sax.py | 10 +++++----- - Lib/test/test_xml_etree.py | 17 ++++++++--------- - 2 files changed, 13 insertions(+), 14 deletions(-) - ---- a/Lib/test/test_sax.py -+++ b/Lib/test/test_sax.py -@@ -1211,10 +1211,9 @@ class ExpatReaderTest(XmlTestBase): - - self.assertEqual(result.getvalue(), start + b"text") - -+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), -+ "Reparse deferral not defined for libexpat < 2.6.0") - def test_flush_reparse_deferral_enabled(self): -- if pyexpat.version_info < (2, 6, 0): -- self.skipTest(f'Expat {pyexpat.version_info} does not support reparse deferral') -- - result = BytesIO() - xmlgen = XMLGenerator(result) - parser = create_parser() -@@ -1236,6 +1235,8 @@ class ExpatReaderTest(XmlTestBase): - - self.assertEqual(result.getvalue(), start + b"") - -+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), -+ "Reparse deferral not defined for libexpat < 2.6.0") - def test_flush_reparse_deferral_disabled(self): - result = BytesIO() - xmlgen = XMLGenerator(result) -@@ -1245,8 +1246,7 @@ class ExpatReaderTest(XmlTestBase): - for chunk in (""): - parser.feed(chunk) - -- if pyexpat.version_info >= (2, 6, 0): -- parser._parser.SetReparseDeferralEnabled(False) -+ parser._parser.SetReparseDeferralEnabled(False) - - self.assertEqual(result.getvalue(), start) # i.e. no elements started - self.assertFalse(parser._parser.GetReparseDeferralEnabled()) ---- a/Lib/test/test_xml_etree.py -+++ b/Lib/test/test_xml_etree.py -@@ -1619,11 +1619,9 @@ class XMLPullParserTest(unittest.TestCas - with self.assertRaises(ValueError): - ET.XMLPullParser(events=('start', 'end', 'bogus')) - -+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), -+ "Reparse deferral not defined for libexpat < 2.6.0") - def test_flush_reparse_deferral_enabled(self): -- if pyexpat.version_info < (2, 6, 0): -- self.skipTest(f'Expat {pyexpat.version_info} does not ' -- 'support reparse deferral') -- - parser = ET.XMLPullParser(events=('start', 'end')) - - for chunk in (""): -@@ -1644,17 +1642,18 @@ class XMLPullParserTest(unittest.TestCas - - self.assert_event_tags(parser, [('end', 'doc')]) - -+ @unittest.skipIf(pyexpat.version_info < (2, 6, 0), -+ "Reparse deferral not defined for libexpat < 2.6.0") - def test_flush_reparse_deferral_disabled(self): - parser = ET.XMLPullParser(events=('start', 'end')) - - for chunk in (""): - parser.feed(chunk) - -- if pyexpat.version_info >= (2, 6, 0): -- if not ET is pyET: -- self.skipTest(f'XMLParser.(Get|Set)ReparseDeferralEnabled ' -- 'methods not available in C') -- parser._parser._parser.SetReparseDeferralEnabled(False) -+ if not ET is pyET: -+ self.skipTest(f'XMLParser.(Get|Set)ReparseDeferralEnabled ' -+ 'methods not available in C') -+ parser._parser._parser.SetReparseDeferralEnabled(False) - - self.assert_event_tags(parser, []) # i.e. no elements started - if ET is pyET: diff --git a/python39.changes b/python39.changes index 4ab27ce..31d0635 100644 --- a/python39.changes +++ b/python39.changes @@ -1,3 +1,25 @@ +------------------------------------------------------------------- +Thu Sep 5 13:44:48 UTC 2024 - Matej Cepl + +- Add CVE-2024-6232-cookies-quad-complex.patch to avoid quadratic + complexity in parsing "-quoted cookie values with backslashes + (bsc#1229596, CVE-2024-6232). + +------------------------------------------------------------------- +Thu Sep 5 08:11:45 UTC 2024 - Matej Cepl + +- Add CVE-2023-52425-libexpat-2.6.0-backport.patch to fix tests with + patched libexpat below 2.6.0 that doesn't update the version number, + just in SLE. +- Remove old-libexpat.patch, of course. + +------------------------------------------------------------------- +Mon Sep 2 09:44:26 UTC 2024 - Matej Cepl + +- Add gh120226-fix-sendfile-test-kernel-610.patch to avoid + failing test_sendfile_close_peer_in_the_middle_of_receiving + tests on Linux >= 6.10 (GH-120227). + ------------------------------------------------------------------- Wed Aug 28 16:54:34 UTC 2024 - Matej Cepl diff --git a/python39.spec b/python39.spec index 751d35c..dfa2e3f 100644 --- a/python39.spec +++ b/python39.spec @@ -164,6 +164,9 @@ Patch34: skip-test_pyobject_freed_is_freed.patch # PATCH-FIX-UPSTREAM support-expat-CVE-2022-25236-patched.patch jsc#SLE-21253 mcepl@suse.com # Makes Python resilient to changes of API of libexpat Patch35: support-expat-CVE-2022-25236-patched.patch +# PATCH-FIX-UPSTREAM CVE-2023-52425-libexpat-2.6.0-backport.patch gh#python/cpython#117187 mcepl@suse.com +# Make the test suite work with libexpat < 2.6.0 +Patch36: CVE-2023-52425-libexpat-2.6.0-backport.patch # PATCH-FIX-UPSTREAM 98437-sphinx.locale._-as-gettext-in-pyspecific.patch gh#python/cpython#98366 mcepl@suse.com # this patch makes things totally awesome Patch37: 98437-sphinx.locale._-as-gettext-in-pyspecific.patch @@ -184,9 +187,6 @@ Patch41: downport-Sphinx-features.patch # indicate the parsing error (old API), from gh#python/cpython!105127 # Patch carries a REGRESSION (gh#python/cpython#106669), so it has been also partially REVERTED Patch42: CVE-2023-27043-email-parsing-errors.patch -# PATCH-FIX-UPSTREAM old-libexpat.patch gh#python/cpython#117187 mcepl@suse.com -# Make the test suite work with libexpat < 2.6.0 -Patch43: old-libexpat.patch # PATCH-FIX-UPSTREAM CVE-2024-0397-memrace_ssl.SSLContext_cert_store.patch bsc#1226447 mcepl@suse.com # removes memory race condition in ssl.SSLContext certificate store methods Patch44: CVE-2024-0397-memrace_ssl.SSLContext_cert_store.patch @@ -205,6 +205,12 @@ Patch48: CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch # PATCH-FIX-UPSTREAM CVE-2024-8088-inf-loop-zipfile_Path.patch bsc#1229704 mcepl@suse.com # avoid denial of service in zipfile Patch49: CVE-2024-8088-inf-loop-zipfile_Path.patch +# PATCH-FIX-UPSTREAM gh120226-fix-sendfile-test-kernel-610.patch gh#python/cpython#120226 mcepl@suse.com +# Fix test_sendfile_close_peer_in_the_middle_of_receiving on Linux >= 6.10 (GH-120227) +Patch50: gh120226-fix-sendfile-test-kernel-610.patch +# PATCH-FIX-UPSTREAM CVE-2024-6232-cookies-quad-complex.patch bsc#1229596 mcepl@suse.com +# avoid quadratic complexity in parsing "-quoted cookie values with backslashes +Patch51: CVE-2024-6232-cookies-quad-complex.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -460,6 +466,7 @@ other applications. %patch -P 05 -p1 %endif %patch -P 35 -p1 +%patch -P 36 -p1 %patch -P 37 -p1 %patch -P 38 -p1 %patch -P 39 -p1 @@ -468,13 +475,14 @@ other applications. %patch -p1 -P 41 %endif %patch -p1 -P 42 -%patch -p1 -P 43 %patch -p1 -P 44 %patch -p1 -P 45 %patch -p1 -P 46 %patch -p1 -P 47 %patch -p1 -P 48 %patch -p1 -P 49 +%patch -p1 -P 50 +%patch -p1 -P 51 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac