From b2be1494ec96c819272f8fca34bd5a7f32951acd Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Thu, 16 Apr 2026 05:33:50 +0200 Subject: [PATCH] Security fixes for CVE-2026-1502, CVE-2026-4786, CVE-2026-6100, CVE-2026-2297, CVE-2026-3644, CVE-2026-4224 Resolves: RHEL-167886, RHEL-168120 --- 00479-cve-2026-1502.patch | 107 ++++++++++++++++++++++++++++ 00480-cve-2026-4786.patch | 64 +++++++++++++++++ 00482-cve-2026-6100.patch | 61 ++++++++++++++++ 00483-cve-2026-2297.patch | 33 +++++++++ 00484-cve-2026-3644.patch | 146 ++++++++++++++++++++++++++++++++++++++ 00485-cve-2026-4224.patch | 98 +++++++++++++++++++++++++ python3.12.spec | 42 ++++++++++- 7 files changed, 550 insertions(+), 1 deletion(-) create mode 100644 00479-cve-2026-1502.patch create mode 100644 00480-cve-2026-4786.patch create mode 100644 00482-cve-2026-6100.patch create mode 100644 00483-cve-2026-2297.patch create mode 100644 00484-cve-2026-3644.patch create mode 100644 00485-cve-2026-4224.patch diff --git a/00479-cve-2026-1502.patch b/00479-cve-2026-1502.patch new file mode 100644 index 0000000..16dc99b --- /dev/null +++ b/00479-cve-2026-1502.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Seth Larson +Date: Fri, 10 Apr 2026 10:21:42 -0500 +Subject: 00479: CVE-2026-1502 + +Reject CR/LF in HTTP tunnel request headers + +Co-authored-by: Illia Volochii +--- + Lib/http/client.py | 11 ++++- + Lib/test/test_httplib.py | 45 +++++++++++++++++++ + ...-03-20-09-29-42.gh-issue-146211.PQVbs7.rst | 2 + + 3 files changed, 57 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst + +diff --git a/Lib/http/client.py b/Lib/http/client.py +index 70451d67d4..7db4807b30 100644 +--- a/Lib/http/client.py ++++ b/Lib/http/client.py +@@ -972,13 +972,22 @@ def _wrap_ipv6(self, ip): + return ip + + def _tunnel(self): ++ if _contains_disallowed_url_pchar_re.search(self._tunnel_host): ++ raise ValueError('Tunnel host can\'t contain control characters %r' ++ % (self._tunnel_host,)) + connect = b"CONNECT %s:%d %s\r\n" % ( + self._wrap_ipv6(self._tunnel_host.encode("idna")), + self._tunnel_port, + self._http_vsn_str.encode("ascii")) + headers = [connect] + for header, value in self._tunnel_headers.items(): +- headers.append(f"{header}: {value}\r\n".encode("latin-1")) ++ header_bytes = header.encode("latin-1") ++ value_bytes = value.encode("latin-1") ++ if not _is_legal_header_name(header_bytes): ++ raise ValueError('Invalid header name %r' % (header_bytes,)) ++ if _is_illegal_header_value(value_bytes): ++ raise ValueError('Invalid header value %r' % (value_bytes,)) ++ headers.append(b"%s: %s\r\n" % (header_bytes, value_bytes)) + headers.append(b"\r\n") + # Making a single send() call instead of one per line encourages + # the host OS to use a more optimal packet size instead of +diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py +index e46dac0077..e027d930d9 100644 +--- a/Lib/test/test_httplib.py ++++ b/Lib/test/test_httplib.py +@@ -369,6 +369,51 @@ def test_invalid_headers(self): + with self.assertRaisesRegex(ValueError, 'Invalid header'): + conn.putheader(name, value) + ++ def test_invalid_tunnel_headers(self): ++ cases = ( ++ ('Invalid\r\nName', 'ValidValue'), ++ ('Invalid\rName', 'ValidValue'), ++ ('Invalid\nName', 'ValidValue'), ++ ('\r\nInvalidName', 'ValidValue'), ++ ('\rInvalidName', 'ValidValue'), ++ ('\nInvalidName', 'ValidValue'), ++ (' InvalidName', 'ValidValue'), ++ ('\tInvalidName', 'ValidValue'), ++ ('Invalid:Name', 'ValidValue'), ++ (':InvalidName', 'ValidValue'), ++ ('ValidName', 'Invalid\r\nValue'), ++ ('ValidName', 'Invalid\rValue'), ++ ('ValidName', 'Invalid\nValue'), ++ ('ValidName', 'InvalidValue\r\n'), ++ ('ValidName', 'InvalidValue\r'), ++ ('ValidName', 'InvalidValue\n'), ++ ) ++ for name, value in cases: ++ with self.subTest((name, value)): ++ conn = client.HTTPConnection('example.com') ++ conn.set_tunnel('tunnel', headers={ ++ name: value ++ }) ++ conn.sock = FakeSocket('') ++ with self.assertRaisesRegex(ValueError, 'Invalid header'): ++ conn._tunnel() # Called in .connect() ++ ++ def test_invalid_tunnel_host(self): ++ cases = ( ++ 'invalid\r.host', ++ '\ninvalid.host', ++ 'invalid.host\r\n', ++ 'invalid.host\x00', ++ 'invalid host', ++ ) ++ for tunnel_host in cases: ++ with self.subTest(tunnel_host): ++ conn = client.HTTPConnection('example.com') ++ conn.set_tunnel(tunnel_host) ++ conn.sock = FakeSocket('') ++ with self.assertRaisesRegex(ValueError, 'Tunnel host can\'t contain control characters'): ++ conn._tunnel() # Called in .connect() ++ + def test_headers_debuglevel(self): + body = ( + b'HTTP/1.1 200 OK\r\n' +diff --git a/Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst b/Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst +new file mode 100644 +index 0000000000..4993633b8e +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-03-20-09-29-42.gh-issue-146211.PQVbs7.rst +@@ -0,0 +1,2 @@ ++Reject CR/LF characters in tunnel request headers for the ++HTTPConnection.set_tunnel() method. diff --git a/00480-cve-2026-4786.patch b/00480-cve-2026-4786.patch new file mode 100644 index 0000000..73e4e13 --- /dev/null +++ b/00480-cve-2026-4786.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Stan Ulbrych +Date: Mon, 13 Apr 2026 20:02:52 +0100 +Subject: 00480: CVE-2026-4786 + +Fix webbrowser `%action` substitution bypass of dash-prefix check +--- + Lib/test/test_webbrowser.py | 9 +++++++++ + Lib/webbrowser.py | 5 +++-- + .../2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst | 2 ++ + 3 files changed, 14 insertions(+), 2 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst + +diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py +index 60f094fd6a..e900c0212b 100644 +--- a/Lib/test/test_webbrowser.py ++++ b/Lib/test/test_webbrowser.py +@@ -99,6 +99,15 @@ def test_open_new_tab(self): + options=[], + arguments=[URL]) + ++ def test_reject_action_dash_prefixes(self): ++ browser = self.browser_class(name=CMD_NAME) ++ with self.assertRaises(ValueError): ++ browser.open('%action--incognito') ++ # new=1: action is "--new-window", so "%action" itself expands to ++ # a dash-prefixed flag even with no dash in the original URL. ++ with self.assertRaises(ValueError): ++ browser.open('%action', new=1) ++ + + class EdgeCommandTest(CommandTestMixin, unittest.TestCase): + +diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py +index 0bdb644d7d..79d410bcae 100755 +--- a/Lib/webbrowser.py ++++ b/Lib/webbrowser.py +@@ -268,7 +268,6 @@ def _invoke(self, args, remote, autoraise, url=None): + + def open(self, url, new=0, autoraise=True): + sys.audit("webbrowser.open", url) +- self._check_url(url) + if new == 0: + action = self.remote_action + elif new == 1: +@@ -282,7 +281,9 @@ def open(self, url, new=0, autoraise=True): + raise Error("Bad 'new' parameter to open(); " + + "expected 0, 1, or 2, got %s" % new) + +- args = [arg.replace("%s", url).replace("%action", action) ++ self._check_url(url.replace("%action", action)) ++ ++ args = [arg.replace("%action", action).replace("%s", url) + for arg in self.remote_args] + args = [arg for arg in args if arg] + success = self._invoke(args, True, autoraise, url) +diff --git a/Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst b/Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst +new file mode 100644 +index 0000000000..45cdeebe1b +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-03-31-09-15-51.gh-issue-148169.EZJzz2.rst +@@ -0,0 +1,2 @@ ++A bypass in :mod:`webbrowser` allowed URLs prefixed with ``%action`` to pass ++the dash-prefix safety check. diff --git a/00482-cve-2026-6100.patch b/00482-cve-2026-6100.patch new file mode 100644 index 0000000..5656e3d --- /dev/null +++ b/00482-cve-2026-6100.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Stan Ulbrych +Date: Mon, 13 Apr 2026 02:14:54 +0100 +Subject: 00482: CVE-2026-6100 + +Fix a possible UAF in {LZMA,BZ2,_Zlib}Decompressor +--- + .../Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst | 5 +++++ + Modules/_bz2module.c | 1 + + Modules/_lzmamodule.c | 1 + + Modules/zlibmodule.c | 1 + + 4 files changed, 8 insertions(+) + create mode 100644 Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst + +diff --git a/Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst b/Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst +new file mode 100644 +index 0000000000..9502189ab1 +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-04-10-16-28-21.gh-issue-148395.kfzm0G.rst +@@ -0,0 +1,5 @@ ++Fix a dangling input pointer in :class:`lzma.LZMADecompressor`, ++:class:`bz2.BZ2Decompressor`, and internal :class:`!zlib._ZlibDecompressor` ++when memory allocation fails with :exc:`MemoryError`, which could let a ++subsequent :meth:`!decompress` call read or write through a stale pointer to ++the already-released caller buffer. +diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c +index 97bd44b4ac..a732e89d55 100644 +--- a/Modules/_bz2module.c ++++ b/Modules/_bz2module.c +@@ -587,6 +587,7 @@ decompress(BZ2Decompressor *d, char *data, size_t len, Py_ssize_t max_length) + return result; + + error: ++ bzs->next_in = NULL; + Py_XDECREF(result); + return NULL; + } +diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c +index 7bbd6569aa..103a6ef86c 100644 +--- a/Modules/_lzmamodule.c ++++ b/Modules/_lzmamodule.c +@@ -1114,6 +1114,7 @@ decompress(Decompressor *d, uint8_t *data, size_t len, Py_ssize_t max_length) + return result; + + error: ++ lzs->next_in = NULL; + Py_XDECREF(result); + return NULL; + } +diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c +index f94c57e4c8..9759593b6a 100644 +--- a/Modules/zlibmodule.c ++++ b/Modules/zlibmodule.c +@@ -1645,6 +1645,7 @@ decompress(ZlibDecompressor *self, uint8_t *data, + return result; + + error: ++ self->zst.next_in = NULL; + Py_XDECREF(result); + return NULL; + } diff --git a/00483-cve-2026-2297.patch b/00483-cve-2026-2297.patch new file mode 100644 index 0000000..8b504c9 --- /dev/null +++ b/00483-cve-2026-2297.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Steve Dower +Date: Wed, 4 Mar 2026 19:55:52 +0000 +Subject: 00483: CVE-2026-2297 + +Logging Bypass in Legacy .pyc File Handling +--- + Lib/importlib/_bootstrap_external.py | 2 +- + .../Security/2026-03-04-18-59-17.gh-issue-145506.6hwvEh.rst | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Security/2026-03-04-18-59-17.gh-issue-145506.6hwvEh.rst + +diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py +index 9b8a8dfc5a..6e4a087a10 100644 +--- a/Lib/importlib/_bootstrap_external.py ++++ b/Lib/importlib/_bootstrap_external.py +@@ -1186,7 +1186,7 @@ def get_filename(self, fullname): + + def get_data(self, path): + """Return the data from path as raw bytes.""" +- if isinstance(self, (SourceLoader, ExtensionFileLoader)): ++ if isinstance(self, (SourceLoader, SourcelessFileLoader, ExtensionFileLoader)): + with _io.open_code(str(path)) as file: + return file.read() + else: +diff --git a/Misc/NEWS.d/next/Security/2026-03-04-18-59-17.gh-issue-145506.6hwvEh.rst b/Misc/NEWS.d/next/Security/2026-03-04-18-59-17.gh-issue-145506.6hwvEh.rst +new file mode 100644 +index 0000000000..dcdb44d4fa +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-03-04-18-59-17.gh-issue-145506.6hwvEh.rst +@@ -0,0 +1,2 @@ ++Fixes :cve:`2026-2297` by ensuring that ``SourcelessFileLoader`` uses ++:func:`io.open_code` when opening ``.pyc`` files. diff --git a/00484-cve-2026-3644.patch b/00484-cve-2026-3644.patch new file mode 100644 index 0000000..a1c12bd --- /dev/null +++ b/00484-cve-2026-3644.patch @@ -0,0 +1,146 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> +Date: Mon, 16 Mar 2026 13:43:43 +0000 +Subject: 00484: CVE-2026-3644 + +Incomplete control character validation in http.cookies + +Co-authored-by: Victor Stinner +--- + Lib/http/cookies.py | 24 ++++++++++-- + Lib/test/test_http_cookies.py | 38 +++++++++++++++++++ + ...-03-06-17-03-38.gh-issue-145599.kchwZV.rst | 4 ++ + 3 files changed, 62 insertions(+), 4 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst + +diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py +index d0a69cbe19..63d119ad46 100644 +--- a/Lib/http/cookies.py ++++ b/Lib/http/cookies.py +@@ -335,9 +335,16 @@ def update(self, values): + key = key.lower() + if key not in self._reserved: + raise CookieError("Invalid attribute %r" % (key,)) ++ if _has_control_character(key, val): ++ raise CookieError("Control characters are not allowed in " ++ f"cookies {key!r} {val!r}") + data[key] = val + dict.update(self, data) + ++ def __ior__(self, values): ++ self.update(values) ++ return self ++ + def isReservedKey(self, K): + return K.lower() in self._reserved + +@@ -363,9 +370,15 @@ def __getstate__(self): + } + + def __setstate__(self, state): +- self._key = state['key'] +- self._value = state['value'] +- self._coded_value = state['coded_value'] ++ key = state['key'] ++ value = state['value'] ++ coded_value = state['coded_value'] ++ if _has_control_character(key, value, coded_value): ++ raise CookieError("Control characters are not allowed in cookies " ++ f"{key!r} {value!r} {coded_value!r}") ++ self._key = key ++ self._value = value ++ self._coded_value = coded_value + + def output(self, attrs=None, header="Set-Cookie:"): + return "%s %s" % (header, self.OutputString(attrs)) +@@ -377,13 +390,16 @@ def __repr__(self): + + def js_output(self, attrs=None): + # Print javascript ++ output_string = self.OutputString(attrs) ++ if _has_control_character(output_string): ++ raise CookieError("Control characters are not allowed in cookies") + return """ + +- """ % (self.OutputString(attrs).replace('"', r'\"')) ++ """ % (output_string.replace('"', r'\"')) + + def OutputString(self, attrs=None): + # Build up our result +diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py +index f196bcc48e..2478a6c630 100644 +--- a/Lib/test/test_http_cookies.py ++++ b/Lib/test/test_http_cookies.py +@@ -573,6 +573,14 @@ def test_control_characters(self): + with self.assertRaises(cookies.CookieError): + morsel["path"] = c0 + ++ # .__setstate__() ++ with self.assertRaises(cookies.CookieError): ++ morsel.__setstate__({'key': c0, 'value': 'val', 'coded_value': 'coded'}) ++ with self.assertRaises(cookies.CookieError): ++ morsel.__setstate__({'key': 'key', 'value': c0, 'coded_value': 'coded'}) ++ with self.assertRaises(cookies.CookieError): ++ morsel.__setstate__({'key': 'key', 'value': 'val', 'coded_value': c0}) ++ + # .setdefault() + with self.assertRaises(cookies.CookieError): + morsel.setdefault("path", c0) +@@ -587,6 +595,18 @@ def test_control_characters(self): + with self.assertRaises(cookies.CookieError): + morsel.set("path", "val", c0) + ++ # .update() ++ with self.assertRaises(cookies.CookieError): ++ morsel.update({"path": c0}) ++ with self.assertRaises(cookies.CookieError): ++ morsel.update({c0: "val"}) ++ ++ # .__ior__() ++ with self.assertRaises(cookies.CookieError): ++ morsel |= {"path": c0} ++ with self.assertRaises(cookies.CookieError): ++ morsel |= {c0: "val"} ++ + def test_control_characters_output(self): + # Tests that even if the internals of Morsel are modified + # that a call to .output() has control character safeguards. +@@ -607,6 +627,24 @@ def test_control_characters_output(self): + with self.assertRaises(cookies.CookieError): + cookie.output() + ++ # Tests that .js_output() also has control character safeguards. ++ for c0 in support.control_characters_c0(): ++ morsel = cookies.Morsel() ++ morsel.set("key", "value", "coded-value") ++ morsel._key = c0 # Override private variable. ++ cookie = cookies.SimpleCookie() ++ cookie["cookie"] = morsel ++ with self.assertRaises(cookies.CookieError): ++ cookie.js_output() ++ ++ morsel = cookies.Morsel() ++ morsel.set("key", "value", "coded-value") ++ morsel._coded_value = c0 # Override private variable. ++ cookie = cookies.SimpleCookie() ++ cookie["cookie"] = morsel ++ with self.assertRaises(cookies.CookieError): ++ cookie.js_output() ++ + + def load_tests(loader, tests, pattern): + tests.addTest(doctest.DocTestSuite(cookies)) +diff --git a/Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst b/Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst +new file mode 100644 +index 0000000000..e53a932d12 +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst +@@ -0,0 +1,4 @@ ++Reject control characters in :class:`http.cookies.Morsel` ++:meth:`~http.cookies.Morsel.update` and ++:meth:`~http.cookies.BaseCookie.js_output`. ++This addresses :cve:`2026-3644`. diff --git a/00485-cve-2026-4224.patch b/00485-cve-2026-4224.patch new file mode 100644 index 0000000..14f8734 --- /dev/null +++ b/00485-cve-2026-4224.patch @@ -0,0 +1,98 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> +Date: Sun, 15 Mar 2026 21:46:06 +0000 +Subject: 00485: CVE-2026-4224 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Stack overflow parsing XML with deeply nested DTD content models + +Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> +--- + Lib/test/test_pyexpat.py | 18 ++++++++++++++++++ + ...6-03-14-17-31-39.gh-issue-145986.ifSSr8.rst | 4 ++++ + Modules/pyexpat.c | 9 ++++++++- + 3 files changed, 30 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst + +diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py +index 38f951573f..37d9086f40 100644 +--- a/Lib/test/test_pyexpat.py ++++ b/Lib/test/test_pyexpat.py +@@ -675,6 +675,24 @@ def test_change_size_2(self): + parser.Parse(xml2, True) + self.assertEqual(self.n, 4) + ++class ElementDeclHandlerTest(unittest.TestCase): ++ def test_deeply_nested_content_model(self): ++ # This should raise a RecursionError and not crash. ++ # See https://github.com/python/cpython/issues/145986. ++ N = 500_000 ++ data = ( ++ b'\n]>\n\n' ++ ) ++ ++ parser = expat.ParserCreate() ++ parser.ElementDeclHandler = lambda _1, _2: None ++ with support.infinite_recursion(): ++ with self.assertRaises(RecursionError): ++ parser.Parse(data) ++ ++ + class MalformedInputTest(unittest.TestCase): + def test1(self): + xml = b"\0\r\n" +diff --git a/Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst b/Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst +new file mode 100644 +index 0000000000..79536d1fef +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst +@@ -0,0 +1,4 @@ ++:mod:`xml.parsers.expat`: Fixed a crash caused by unbounded C recursion when ++converting deeply nested XML content models with ++:meth:`~xml.parsers.expat.xmlparser.ElementDeclHandler`. ++This addresses :cve:`2026-4224`. +diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c +index 79492ca5c4..8673540f35 100644 +--- a/Modules/pyexpat.c ++++ b/Modules/pyexpat.c +@@ -3,6 +3,7 @@ + #endif + + #include "Python.h" ++#include "pycore_ceval.h" // _Py_EnterRecursiveCall() + #include "pycore_runtime.h" // _Py_ID() + #include + +@@ -578,6 +579,10 @@ static PyObject * + conv_content_model(XML_Content * const model, + PyObject *(*conv_string)(const XML_Char *)) + { ++ if (_Py_EnterRecursiveCall(" in conv_content_model")) { ++ return NULL; ++ } ++ + PyObject *result = NULL; + PyObject *children = PyTuple_New(model->numchildren); + int i; +@@ -589,7 +594,7 @@ conv_content_model(XML_Content * const model, + conv_string); + if (child == NULL) { + Py_XDECREF(children); +- return NULL; ++ goto done; + } + PyTuple_SET_ITEM(children, i, child); + } +@@ -597,6 +602,8 @@ conv_content_model(XML_Content * const model, + model->type, model->quant, + conv_string,model->name, children); + } ++done: ++ _Py_LeaveRecursiveCall(); + return result; + } + diff --git a/python3.12.spec b/python3.12.spec index 9bd429d..7f3fda2 100644 --- a/python3.12.spec +++ b/python3.12.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 1%{?dist} +Release: 2%{?dist} License: Python-2.0.1 @@ -459,6 +459,42 @@ Patch475: 00475-cve-2025-15367.patch # Reject leading dashes in webbrowser URLs (GH-146360) Patch478: 00478-cve-2026-4519.patch +# 00479 # 97404b2cf62e545c2d41be7ccfed4e74da9ee665 +# CVE-2026-1502 +# +# Reject CR/LF in HTTP tunnel request headers +Patch479: 00479-cve-2026-1502.patch + +# 00480 # 6f4eef3ba4d9818a53698e994550ee8db17a1e2e +# CVE-2026-4786 +# +# Fix webbrowser `%%action` substitution bypass of dash-prefix check +Patch480: 00480-cve-2026-4786.patch + +# 00482 # 69f14bc306fc62400d45565faa980b77858b9151 +# CVE-2026-6100 +# +# Fix a possible UAF in {LZMA,BZ2,_Zlib}Decompressor +Patch482: 00482-cve-2026-6100.patch + +# 00483 # 577c595137ce6ff92158ddaf2d7b7ea86437825d +# CVE-2026-2297 +# +# Logging Bypass in Legacy .pyc File Handling +Patch483: 00483-cve-2026-2297.patch + +# 00484 # 8b5133c1ab17a060cd134bea2a4b6e1831c47fed +# CVE-2026-3644 +# +# Incomplete control character validation in http.cookies +Patch484: 00484-cve-2026-3644.patch + +# 00485 # 12a5b206676927bcee131ab4f2bd6783d2f5914a +# CVE-2026-4224 +# +# Stack overflow parsing XML with deeply nested DTD content models +Patch485: 00485-cve-2026-4224.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1830,6 +1866,10 @@ CheckPython optimized # ====================================================== %changelog +* Thu Apr 16 2026 Charalampos Stratakis - 3.12.13-2 +- Security fixes for CVE-2026-1502, CVE-2026-4786, CVE-2026-6100, CVE-2026-2297, CVE-2026-3644, CVE-2026-4224 +Resolves: RHEL-167886, RHEL-168120 + * Thu Apr 16 2026 Tomáš Hrnčiar - 3.12.13-1 - Update to 3.12.13 - Security fixes for CVE-2025-6075, CVE-2025-13837, CVE-2025-15282, CVE-2025-59375, CVE-2026-0672