diff --git a/00251-change-user-install-location.patch b/00251-change-user-install-location.patch index 7fc8844..7289b79 100644 --- a/00251-change-user-install-location.patch +++ b/00251-change-user-install-location.patch @@ -30,7 +30,7 @@ Co-authored-by: Lumír Balhar 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/Lib/site.py b/Lib/site.py -index aedf36399c..2ae49a8029 100644 +index 041dca113a..ca6320df2f 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -415,8 +415,15 @@ def getsitepackages(prefixes=None): @@ -51,7 +51,7 @@ index aedf36399c..2ae49a8029 100644 if os.path.isdir(sitedir): addsitedir(sitedir, known_paths) diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index f7bd675bb3..f1ff347c3d 100644 +index 43edebce34..40e85568ea 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -106,6 +106,12 @@ @@ -130,7 +130,7 @@ index f7bd675bb3..f1ff347c3d 100644 # On Windows we want to substitute 'lib' for schemes rather # than the native value (without modifying vars, in case it diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index 9723300f51..310706652a 100644 +index ce17206a3c..fd95071099 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -130,8 +130,19 @@ def test_get_path(self): @@ -154,7 +154,7 @@ index 9723300f51..310706652a 100644 os.path.normpath(expected), ) -@@ -393,7 +404,7 @@ def test_get_config_h_filename(self): +@@ -395,7 +406,7 @@ def test_get_config_h_filename(self): self.assertTrue(os.path.isfile(config_h), config_h) def test_get_scheme_names(self): @@ -163,7 +163,7 @@ index 9723300f51..310706652a 100644 if HAS_USER_BASE: wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) -@@ -405,6 +416,8 @@ def test_symlink(self): # Issue 7880 +@@ -407,6 +418,8 @@ def test_symlink(self): # Issue 7880 cmd = "-c", "import sysconfig; print(sysconfig.get_platform())" self.assertEqual(py.call_real(*cmd), py.call_link(*cmd)) diff --git a/00461-downstream-only-install-wheel-in-test-venvs-when-setuptools-71.patch b/00461-downstream-only-install-wheel-in-test-venvs-when-setuptools-71.patch deleted file mode 100644 index ee4cc9a..0000000 --- a/00461-downstream-only-install-wheel-in-test-venvs-when-setuptools-71.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= -Date: Fri, 25 Apr 2025 09:33:37 +0200 -Subject: 00461: Downstream only: Install wheel in test venvs when setuptools < - 71 - ---- - Lib/test/support/__init__.py | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index 4605938b87..a0055d6887 100644 ---- a/Lib/test/support/__init__.py -+++ b/Lib/test/support/__init__.py -@@ -2462,9 +2462,16 @@ def run_command(cmd): - else: - python = os.path.join(venv, 'bin', python_exe) - -+ setuptools_whl = _findwheel('setuptools') -+ whl_filename = os.path.basename(setuptools_whl) -+ setuptools_major = int(whl_filename.split('-')[1].split('.')[0]) -+ if setuptools_major >= 71: # we need 70.1+, but that's OK -+ wheels = (setuptools_whl,) -+ else: -+ wheels = (setuptools_whl, _findwheel('wheel')) - cmd = (python, '-X', 'dev', - '-m', 'pip', 'install', -- _findwheel('setuptools'), -+ *wheels, - ) - run_command(cmd) - diff --git a/00464-enable-pac-and-bti-protections-for-aarch64.patch b/00464-enable-pac-and-bti-protections-for-aarch64.patch deleted file mode 100644 index 1a7acf3..0000000 --- a/00464-enable-pac-and-bti-protections-for-aarch64.patch +++ /dev/null @@ -1,102 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Charalampos Stratakis -Date: Tue, 3 Jun 2025 03:02:15 +0200 -Subject: 00464: Enable PAC and BTI protections for aarch64 - -Apply protection against ROP/JOP attacks for aarch64 on asm_trampoline.S - -The BTI flag must be applied in the assembler sources for this class -of attacks to be mitigated on newer aarch64 processors. - -Upstream PR: https://github.com/python/cpython/pull/130864/files - -The upstream patch is incomplete but only for the case where -frame pointers are not used on 3.13+. - -Since on Fedora we always compile with frame pointers the BTI/PAC -hardware protections can be enabled without losing Perf unwinding. ---- - Python/asm_trampoline.S | 4 +++ - Python/asm_trampoline_aarch64.h | 50 +++++++++++++++++++++++++++++++++ - 2 files changed, 54 insertions(+) - create mode 100644 Python/asm_trampoline_aarch64.h - -diff --git a/Python/asm_trampoline.S b/Python/asm_trampoline.S -index a14e68c0e8..2513cde4e7 100644 ---- a/Python/asm_trampoline.S -+++ b/Python/asm_trampoline.S -@@ -1,3 +1,5 @@ -+#include "asm_trampoline_aarch64.h" -+ - .text - .globl _Py_trampoline_func_start - # The following assembly is equivalent to: -@@ -21,10 +23,12 @@ _Py_trampoline_func_start: - #if defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) - // ARM64 little endian, 64bit ABI - // generate with aarch64-linux-gnu-gcc 12.1 -+ SIGN_LR - stp x29, x30, [sp, -16]! - mov x29, sp - blr x3 - ldp x29, x30, [sp], 16 -+ VERIFY_LR - ret - #endif - #ifdef __riscv -diff --git a/Python/asm_trampoline_aarch64.h b/Python/asm_trampoline_aarch64.h -new file mode 100644 -index 0000000000..4b0ec4a7dc ---- /dev/null -+++ b/Python/asm_trampoline_aarch64.h -@@ -0,0 +1,50 @@ -+#ifndef ASM_TRAMPOLINE_AARCH_64_H_ -+#define ASM_TRAMPOLINE_AARCH_64_H_ -+ -+/* -+ * References: -+ * - https://developer.arm.com/documentation/101028/0012/5--Feature-test-macros -+ * - https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst -+ */ -+ -+#if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1 -+ #define BTI_J hint 36 /* bti j: for jumps, IE br instructions */ -+ #define BTI_C hint 34 /* bti c: for calls, IE bl instructions */ -+ #define GNU_PROPERTY_AARCH64_BTI 1 /* bit 0 GNU Notes is for BTI support */ -+#else -+ #define BTI_J -+ #define BTI_C -+ #define GNU_PROPERTY_AARCH64_BTI 0 -+#endif -+ -+#if defined(__ARM_FEATURE_PAC_DEFAULT) -+ #if __ARM_FEATURE_PAC_DEFAULT & 1 -+ #define SIGN_LR hint 25 /* paciasp: sign with the A key */ -+ #define VERIFY_LR hint 29 /* autiasp: verify with the A key */ -+ #elif __ARM_FEATURE_PAC_DEFAULT & 2 -+ #define SIGN_LR hint 27 /* pacibsp: sign with the b key */ -+ #define VERIFY_LR hint 31 /* autibsp: verify with the b key */ -+ #endif -+ #define GNU_PROPERTY_AARCH64_POINTER_AUTH 2 /* bit 1 GNU Notes is for PAC support */ -+#else -+ #define SIGN_LR BTI_C -+ #define VERIFY_LR -+ #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0 -+#endif -+ -+/* Add the BTI and PAC support to GNU Notes section */ -+#if GNU_PROPERTY_AARCH64_BTI != 0 || GNU_PROPERTY_AARCH64_POINTER_AUTH != 0 -+ .pushsection .note.gnu.property, "a"; /* Start a new allocatable section */ -+ .balign 8; /* align it on a byte boundry */ -+ .long 4; /* size of "GNU\0" */ -+ .long 0x10; /* size of descriptor */ -+ .long 0x5; /* NT_GNU_PROPERTY_TYPE_0 */ -+ .asciz "GNU"; -+ .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ -+ .long 4; /* Four bytes of data */ -+ .long (GNU_PROPERTY_AARCH64_BTI|GNU_PROPERTY_AARCH64_POINTER_AUTH); /* BTI or PAC is enabled */ -+ .long 0; /* padding for 8 byte alignment */ -+ .popsection; /* end the section */ -+#endif -+ -+#endif diff --git a/00466-downstream-only-lower-xml_combined_version-threshold-for-reparse-deferral.patch b/00466-downstream-only-lower-xml_combined_version-threshold-for-reparse-deferral.patch new file mode 100644 index 0000000..15de467 --- /dev/null +++ b/00466-downstream-only-lower-xml_combined_version-threshold-for-reparse-deferral.patch @@ -0,0 +1,133 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= +Date: Wed, 12 Aug 2026 15:18:39 +0200 +Subject: 00466: Downstream only: Lower XML_COMBINED_VERSION threshold for + reparse deferral + +RHEL 9 expat 2.5.0 has XML_SetReparseDeferralEnabled backported +via the CVE-2023-52425 fix, but XML_COMBINED_VERSION remains 20500. +CPython's #if XML_COMBINED_VERSION >= 20600 guards compile the setter +as a no-op, so SetReparseDeferralEnabled silently does nothing and +GetReparseDeferralEnabled always returns False, even though the expat +library actually supports (and enables) reparse deferral. + +Lower the threshold from 20600 to 20500 so that CPython uses the +backported function. This makes the Python API actually work on RHEL 9 +and fixes test failures (test_reparse_deferral_disabled, +test_flush_reparse_deferral_disabled, test_simple_xml_chunk_*). + +The spec file BuildRequires expat-devel >= 2.5.0-2 to ensure the +backported function is available. +--- + Lib/test/test_pyexpat.py | 6 +++--- + Lib/test/test_sax.py | 4 ++-- + Lib/test/test_xml_etree.py | 4 ++-- + Modules/pyexpat.c | 6 +++--- + 4 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py +index ae7cec6540..7f73a8c5e7 100644 +--- a/Lib/test/test_pyexpat.py ++++ b/Lib/test/test_pyexpat.py +@@ -1001,7 +1001,7 @@ def test_error_path_no_crash(self): + class ReparseDeferralTest(unittest.TestCase): + def test_getter_setter_round_trip(self): + parser = expat.ParserCreate() +- enabled = (expat.version_info >= (2, 6, 0)) ++ enabled = (expat.version_info >= (2, 5, 0)) + + self.assertIs(parser.GetReparseDeferralEnabled(), enabled) + parser.SetReparseDeferralEnabled(False) +@@ -1010,7 +1010,7 @@ def test_getter_setter_round_trip(self): + self.assertIs(parser.GetReparseDeferralEnabled(), enabled) + + def test_reparse_deferral_enabled(self): +- if expat.version_info < (2, 6, 0): ++ if expat.version_info < (2, 5, 0): + self.skipTest(f'Expat {expat.version_info} does not ' + 'support reparse deferral') + +@@ -1041,7 +1041,7 @@ def start_element(name, _): + + parser = expat.ParserCreate() + parser.StartElementHandler = start_element +- if expat.version_info >= (2, 6, 0): ++ if expat.version_info >= (2, 5, 0): + parser.SetReparseDeferralEnabled(False) + self.assertFalse(parser.GetReparseDeferralEnabled()) + +diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py +index 9b3014a94a..0e38c9488e 100644 +--- a/Lib/test/test_sax.py ++++ b/Lib/test/test_sax.py +@@ -1215,7 +1215,7 @@ def test_expat_incremental_reset(self): + + self.assertEqual(result.getvalue(), start + b"text") + +- @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ @unittest.skipIf(pyexpat.version_info < (2, 5, 0), + f'Expat {pyexpat.version_info} does not ' + 'support reparse deferral') + def test_flush_reparse_deferral_enabled(self): +@@ -1249,7 +1249,7 @@ def test_flush_reparse_deferral_disabled(self): + for chunk in (""): + parser.feed(chunk) + +- if pyexpat.version_info >= (2, 6, 0): ++ if pyexpat.version_info >= (2, 5, 0): + parser._parser.SetReparseDeferralEnabled(False) + self.assertEqual(result.getvalue(), start) # i.e. no elements started + +diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py +index 4a76a5be1e..effb899d7b 100644 +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -1804,7 +1804,7 @@ def test_unknown_event(self): + with self.assertRaisesRegex(ValueError, "unknown event 'bogus'"): + ET.XMLPullParser(events=(x.decode() for x in (b'start', b'end', b'bogus'))) + +- @unittest.skipIf(pyexpat.version_info < (2, 6, 0), ++ @unittest.skipIf(pyexpat.version_info < (2, 5, 0), + f'Expat {pyexpat.version_info} does not ' + 'support reparse deferral') + def test_flush_reparse_deferral_enabled(self): +@@ -1834,7 +1834,7 @@ def test_flush_reparse_deferral_disabled(self): + for chunk in (""): + parser.feed(chunk) + +- if pyexpat.version_info >= (2, 6, 0): ++ if pyexpat.version_info >= (2, 5, 0): + if not ET is pyET: + self.skipTest(f'XMLParser.(Get|Set)ReparseDeferralEnabled ' + 'methods not available in C') +diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c +index 9e5d84eb5e..b008fe66c1 100644 +--- a/Modules/pyexpat.c ++++ b/Modules/pyexpat.c +@@ -781,7 +781,7 @@ pyexpat_xmlparser_SetReparseDeferralEnabled_impl(xmlparseobject *self, + int enabled) + /*[clinic end generated code: output=5ec539e3b63c8c49 input=021eb9e0bafc32c5]*/ + { +-#if XML_COMBINED_VERSION >= 20600 ++#if XML_COMBINED_VERSION >= 20500 + XML_SetReparseDeferralEnabled(self->itself, enabled ? XML_TRUE : XML_FALSE); + self->reparse_deferral_enabled = (bool)enabled; + #endif +@@ -1446,7 +1446,7 @@ newxmlparseobject(pyexpat_state *state, const char *encoding, + self->ns_prefixes = 0; + self->handlers = NULL; + self->intern = Py_XNewRef(intern); +-#if XML_COMBINED_VERSION >= 20600 ++#if XML_COMBINED_VERSION >= 20500 + self->reparse_deferral_enabled = true; + #else + self->reparse_deferral_enabled = false; +@@ -2332,7 +2332,7 @@ pyexpat_exec(PyObject *mod) + #else + capi->SetHashSalt16Bytes = NULL; + #endif +-#if XML_COMBINED_VERSION >= 20600 ++#if XML_COMBINED_VERSION >= 20500 + capi->SetReparseDeferralEnabled = XML_SetReparseDeferralEnabled; + #else + capi->SetReparseDeferralEnabled = NULL; diff --git a/00466-downstream-only-skip-tests-not-working-with-older-expat-version.patch b/00466-downstream-only-skip-tests-not-working-with-older-expat-version.patch deleted file mode 100644 index a5c383a..0000000 --- a/00466-downstream-only-skip-tests-not-working-with-older-expat-version.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Karolina Surma -Date: Tue, 24 Jun 2025 11:12:13 +0200 -Subject: 00466: Downstream only: Skip tests not working with older expat - version - -We want to run these tests in Fedora and EPEL 10, but not in EPEL 9, -which has too old version of expat. We set the upper bound version -in the conditionalized skip to a release available in CentOS Stream 10, -which is tested as working. ---- - Lib/test/test_pyexpat.py | 2 ++ - Lib/test/test_sax.py | 2 ++ - Lib/test/test_xml_etree.py | 6 ++++++ - 3 files changed, 10 insertions(+) - -diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py -index a091430463..55ebf34c52 100644 ---- a/Lib/test/test_pyexpat.py -+++ b/Lib/test/test_pyexpat.py -@@ -826,6 +826,8 @@ def start_element(name, _): - - self.assertEqual(started, ['doc']) - -+ @unittest.skipIf(expat.version_info < (2, 7, 1), -+ f"Skip for expat < 2.7.1 (version available in RHEL 10)") - def test_reparse_deferral_disabled(self): - started = [] - -diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py -index 9b3014a94a..90401e0d8f 100644 ---- a/Lib/test/test_sax.py -+++ b/Lib/test/test_sax.py -@@ -1240,6 +1240,8 @@ def test_flush_reparse_deferral_enabled(self): - - self.assertEqual(result.getvalue(), start + b"") - -+ @unittest.skipIf(pyexpat.version_info < (2, 7, 1), -+ f"Skip for expat < 2.7.1 (version available in RHEL 10)") - def test_flush_reparse_deferral_disabled(self): - result = BytesIO() - xmlgen = XMLGenerator(result) -diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py -index 78598b35da..802d1399ea 100644 ---- a/Lib/test/test_xml_etree.py -+++ b/Lib/test/test_xml_etree.py -@@ -1548,9 +1548,13 @@ def test_simple_xml(self, chunk_size=None, flush=False): - self.assert_event_tags(parser, [('end', 'root')]) - self.assertIsNone(parser.close()) - -+ @unittest.skipIf(pyexpat.version_info < (2, 7, 1), -+ f"Skip for expat < 2.7.1 (version available in RHEL 10)") - def test_simple_xml_chunk_1(self): - self.test_simple_xml(chunk_size=1, flush=True) - -+ @unittest.skipIf(pyexpat.version_info < (2, 7, 1), -+ f"Skip for expat < 2.7.1 (version available in RHEL 10)") - def test_simple_xml_chunk_5(self): - self.test_simple_xml(chunk_size=5, flush=True) - -@@ -1777,6 +1781,8 @@ def test_flush_reparse_deferral_enabled(self): - - self.assert_event_tags(parser, [('end', 'doc')]) - -+ @unittest.skipIf(pyexpat.version_info < (2, 7, 1), -+ f"Skip for expat < 2.7.1 (version available in RHEL 10)") - def test_flush_reparse_deferral_disabled(self): - parser = ET.XMLPullParser(events=('start', 'end')) - diff --git a/00475-cve-2025-15367.patch b/00475-cve-2025-15367.patch new file mode 100644 index 0000000..2812c2d --- /dev/null +++ b/00475-cve-2025-15367.patch @@ -0,0 +1,59 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Seth Michael Larson +Date: Tue, 20 Jan 2026 14:46:32 -0600 +Subject: 00475: CVE-2025-15367 + +Reject control characters in POP3 commands +--- + Lib/poplib.py | 2 ++ + Lib/test/test_poplib.py | 8 ++++++++ + .../2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst | 1 + + 3 files changed, 11 insertions(+) + create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst + +diff --git a/Lib/poplib.py b/Lib/poplib.py +index 4469bff44b..b97274c5c3 100644 +--- a/Lib/poplib.py ++++ b/Lib/poplib.py +@@ -122,6 +122,8 @@ def _putline(self, line): + def _putcmd(self, line): + if self._debugging: print('*cmd*', repr(line)) + line = bytes(line, self.encoding) ++ if re.search(b'[\x00-\x1F\x7F]', line): ++ raise ValueError('Control characters not allowed in commands') + self._putline(line) + + +diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py +index eb6dab4015..bd621caee4 100644 +--- a/Lib/test/test_poplib.py ++++ b/Lib/test/test_poplib.py +@@ -18,6 +18,7 @@ + from test.support import asynchat + from test.support import asyncore + from test.support.testcase import ExtraAssertions ++from test.support import control_characters_c0 + + + test_support.requires_working_socket(module=True) +@@ -396,6 +397,13 @@ def test_quit(self): + self.assertIsNone(self.client.sock) + self.assertIsNone(self.client.file) + ++ def test_control_characters(self): ++ for c0 in control_characters_c0(): ++ with self.assertRaises(ValueError): ++ self.client.user(f'user{c0}') ++ with self.assertRaises(ValueError): ++ self.client.pass_(f'{c0}pass') ++ + @requires_ssl + def test_stls_capa(self): + capa = self.client.capa() +diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst +new file mode 100644 +index 0000000000..3cde4df3e0 +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst +@@ -0,0 +1 @@ ++Reject control characters in POP3 commands. diff --git a/00477-raise-an-error-when-importing-stdlib-modules-compiled-for-a-different-python-version.patch b/00477-raise-an-error-when-importing-stdlib-modules-compiled-for-a-different-python-version.patch new file mode 100644 index 0000000..f860aa1 --- /dev/null +++ b/00477-raise-an-error-when-importing-stdlib-modules-compiled-for-a-different-python-version.patch @@ -0,0 +1,173 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Mon, 9 Feb 2026 10:44:21 +0100 +Subject: 00477: Raise an error when importing stdlib modules compiled for a + different Python version + +This is a downstream workaround "implementing" python#137212 - +the mechanism for the check exists in Python 3.15+, where it needs to be +added to the standard library modules. +In Fedora, we need it also in previous Python versions, as we experience +segmentation fault when importing stdlib modules after update while +Python is running. + +_curses, _tkinter, _tracemalloc and readline are not calling PyModuleDef_Init, +which is modified with this patch, hence they need a +direct call to the check function. + +Co-Authored-By: Karolina Surma +--- + Include/moduleobject.h | 43 +++++++++++++++++++++++++++++++++++++++++ + Makefile.pre.in | 3 +++ + Modules/_cursesmodule.c | 6 ++++++ + Modules/_tkinter.c | 6 ++++++ + Modules/_tracemalloc.c | 6 ++++++ + Modules/readline.c | 6 ++++++ + Objects/moduleobject.c | 1 + + 7 files changed, 71 insertions(+) + +diff --git a/Include/moduleobject.h b/Include/moduleobject.h +index 2a17c891dd..64017c666c 100644 +--- a/Include/moduleobject.h ++++ b/Include/moduleobject.h +@@ -116,6 +116,49 @@ struct PyModuleDef { + freefunc m_free; + }; + ++#if defined(_PyHack_check_version_on_modinit) && defined(Py_BUILD_CORE) ++/* The mechanism for the check has been implemented on Python 3.15+: ++ * https://github.com/python/cpython/pull/137212. ++ * In Fedora, we need this in older Pythons too: ++ * if somebody attempts to import a module compiled for a different Python version, ++ * instead of segmentation fault a meaningful error is raised. ++ */ ++PyAPI_DATA(const unsigned long) Py_Version; ++ ++static inline int ++_PyHack_CheckInternalAPIVersion(const char *mod_name) ++{ ++ if (PY_VERSION_HEX != Py_Version) { ++ PyErr_Format( ++ PyExc_ImportError, ++ "internal Python C API version mismatch: " ++ "module %s compiled with %lu.%lu.%lu; " ++ "runtime version is %lu.%lu.%lu", ++ mod_name, ++ (const unsigned long)((PY_VERSION_HEX >> 24) & 0xFF), ++ (const unsigned long)((PY_VERSION_HEX >> 16) & 0xFF), ++ (const unsigned long)((PY_VERSION_HEX >> 8) & 0xFF), ++ (const unsigned long)((Py_Version >> 24) & 0xFF), ++ (const unsigned long)((Py_Version >> 16) & 0xFF), ++ (const unsigned long)((Py_Version >> 8) & 0xFF) ++ ); ++ return -1; ++ } ++ return 0; ++} ++ ++static inline PyObject * ++PyModuleDef_Init_with_check(PyModuleDef *def) ++{ ++ if (_PyHack_CheckInternalAPIVersion(def->m_name) < 0) { ++ return NULL; ++ } ++ return PyModuleDef_Init(def); ++} ++ ++#define PyModuleDef_Init PyModuleDef_Init_with_check ++#endif ++ + #ifdef __cplusplus + } + #endif +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 2363b99a25..f1a07645f0 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -3159,3 +3159,6 @@ MODULE__MULTIBYTECODEC_DEPS=$(srcdir)/Modules/cjkcodecs/multibytecodec.h + # Local Variables: + # mode: makefile + # End: ++ ++# Fedora-specific, downstream only ++PY_STDMODULE_CFLAGS += -D_PyHack_check_version_on_modinit=1 +diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c +index 1996488a0e..cfdffd8ec4 100644 +--- a/Modules/_cursesmodule.c ++++ b/Modules/_cursesmodule.c +@@ -5011,6 +5011,12 @@ curses_destructor(PyObject *op) + PyMODINIT_FUNC + PyInit__curses(void) + { ++ #ifdef _PyHack_check_version_on_modinit ++ if (_PyHack_CheckInternalAPIVersion("_curses") < 0) { ++ return NULL; ++ } ++ #endif ++ + PyObject *m, *d, *v, *c_api_object; + + /* Initialize object type */ +diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c +index 4e8d75e8e1..32e58755aa 100644 +--- a/Modules/_tkinter.c ++++ b/Modules/_tkinter.c +@@ -3499,6 +3499,12 @@ static struct PyModuleDef _tkintermodule = { + PyMODINIT_FUNC + PyInit__tkinter(void) + { ++ #ifdef _PyHack_check_version_on_modinit ++ if (_PyHack_CheckInternalAPIVersion("_tkinter") < 0) { ++ return NULL; ++ } ++ #endif ++ + PyObject *m, *uexe, *cexe; + + tcl_lock = PyThread_allocate_lock(); +diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c +index 0b85187e5f..87f358ed07 100644 +--- a/Modules/_tracemalloc.c ++++ b/Modules/_tracemalloc.c +@@ -215,6 +215,12 @@ static struct PyModuleDef module_def = { + PyMODINIT_FUNC + PyInit__tracemalloc(void) + { ++ #ifdef _PyHack_check_version_on_modinit ++ if (_PyHack_CheckInternalAPIVersion("_tracemalloc") < 0) { ++ return NULL; ++ } ++ #endif ++ + PyObject *m; + m = PyModule_Create(&module_def); + if (m == NULL) +diff --git a/Modules/readline.c b/Modules/readline.c +index 7a612deae4..8b2f47eec1 100644 +--- a/Modules/readline.c ++++ b/Modules/readline.c +@@ -1548,6 +1548,12 @@ static struct PyModuleDef readlinemodule = { + PyMODINIT_FUNC + PyInit_readline(void) + { ++ #ifdef _PyHack_check_version_on_modinit ++ if (_PyHack_CheckInternalAPIVersion("readline") < 0) { ++ return NULL; ++ } ++ #endif ++ + const char *backend = "readline"; + PyObject *m; + readlinestate *mod_state; +diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c +index d787f29004..31175ceb3f 100644 +--- a/Objects/moduleobject.c ++++ b/Objects/moduleobject.c +@@ -43,6 +43,7 @@ _PyModule_IsExtension(PyObject *obj) + } + + ++#undef PyModuleDef_Init + PyObject* + PyModuleDef_Init(PyModuleDef* def) + { diff --git a/plan.fmf b/plan.fmf index 432f1a3..9c9afb8 100644 --- a/plan.fmf +++ b/plan.fmf @@ -43,6 +43,9 @@ discover: - name: marshalparser path: /marshalparser test: "VERSION=${pybasever} SAMPLE=10 ./test_marshalparser_compatibility.sh" + - name: required_symbols + path: /required-symbols + test: "VERSION=${pybasever} ./check.sh" prepare: - name: Install dependencies @@ -62,8 +65,9 @@ prepare: - virtualenv # for virtualenv tests - glibc-all-langpacks # for locale tests - marshalparser # for testing compatibility (magic numbers) with marshalparser + - binutils # for nm (symbol inspection) - rpm # for debugging - - dnf # for upgrade + - dnf # for upgrade and downgrade - name: Update packages how: shell script: dnf upgrade -y diff --git a/python3.13.spec b/python3.13.spec index c24a443..4045e62 100644 --- a/python3.13.spec +++ b/python3.13.spec @@ -45,11 +45,11 @@ URL: https://www.python.org/ # WARNING When rebasing to a new Python version, # remember to update the python3-docs package as well -%global general_version %{pybasever}.8 +%global general_version %{pybasever}.15 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 1%{?dist} +Release: 2%{?dist} License: Python-2.0.1 @@ -109,31 +109,30 @@ License: Python-2.0.1 # This needs to be manually updated when we update Python. # Explore the sources tarball (you need the version before %%prep is executed): # $ tar -tf Python-%%{upstream_version}.tar.xz | grep whl -%global pip_version 25.2 +%global pip_version 26.2.1 %global setuptools_version 79.0.1 # All of those also include a list of indirect bundled libs: # pip # $ %%{_rpmconfigdir}/pythonbundles.py <(unzip -p Lib/ensurepip/_bundled/pip-*.whl pip/_vendor/vendor.txt) %global pip_bundled_provides %{expand: -Provides: bundled(python3dist(cachecontrol)) = 0.14.3 -Provides: bundled(python3dist(certifi)) = 2025.7.14 -Provides: bundled(python3dist(dependency-groups)) = 1.3.1 -Provides: bundled(python3dist(distlib)) = 0.4 +Provides: bundled(python3dist(cachecontrol)) = 0.14.4 +Provides: bundled(python3dist(certifi)) = 2026.6.17 +Provides: bundled(python3dist(distlib)) = 0.4.2 Provides: bundled(python3dist(distro)) = 1.9 -Provides: bundled(python3dist(idna)) = 3.10 -Provides: bundled(python3dist(msgpack)) = 1.1.1 -Provides: bundled(python3dist(packaging)) = 25 -Provides: bundled(python3dist(platformdirs)) = 4.3.8 -Provides: bundled(python3dist(pygments)) = 2.19.2 +Provides: bundled(python3dist(idna)) = 3.18 +Provides: bundled(python3dist(msgpack)) = 1.1.2 +Provides: bundled(python3dist(packaging)) = 26.2 +Provides: bundled(python3dist(platformdirs)) = 4.10 +Provides: bundled(python3dist(pygments)) = 2.20 Provides: bundled(python3dist(pyproject-hooks)) = 1.2 -Provides: bundled(python3dist(requests)) = 2.32.4 -Provides: bundled(python3dist(resolvelib)) = 1.2 -Provides: bundled(python3dist(rich)) = 14.1 +Provides: bundled(python3dist(requests)) = 2.34.2 +Provides: bundled(python3dist(resolvelib)) = 1.2.1 +Provides: bundled(python3dist(rich)) = 14.2 Provides: bundled(python3dist(setuptools)) = 70.3 -Provides: bundled(python3dist(tomli)) = 2.2.1 +Provides: bundled(python3dist(tomli)) = 2.4.1 Provides: bundled(python3dist(tomli-w)) = 1.2 -Provides: bundled(python3dist(truststore)) = 0.10.1 -Provides: bundled(python3dist(urllib3)) = 1.26.20 +Provides: bundled(python3dist(truststore)) = 0.10.4 +Provides: bundled(python3dist(urllib3)) = 2.7 } # setuptools # vendor.txt not in .whl @@ -255,7 +254,7 @@ Obsoletes: python%{pybasever}%{?1:-%{1}}\ BuildRequires: autoconf BuildRequires: bluez-libs-devel BuildRequires: bzip2-devel -BuildRequires: expat-devel +BuildRequires: expat-devel >= 2.5.0-2 BuildRequires: findutils BuildRequires: gcc BuildRequires: gdbm-devel @@ -268,7 +267,6 @@ BuildRequires: libuuid-devel BuildRequires: make BuildRequires: mpdecimal-devel BuildRequires: ncurses-devel -BuildRequires: openssl-devel BuildRequires: pkgconfig BuildRequires: python-rpm-macros BuildRequires: readline-devel @@ -281,6 +279,10 @@ BuildRequires: xz-devel BuildRequires: zlib-devel BuildRequires: /usr/bin/dtrace +# Support for OpenSSL 4 only landed in Python 3.15 for now +# https://github.com/python/cpython/issues/146207 +BuildRequires: (openssl-devel < 1:4 or openssl3-devel) + %if %{undefined rhel} BuildRequires: libb2-devel %endif @@ -368,35 +370,45 @@ Source11: idle3.appdata.xml # pypa/distutils integration: https://github.com/pypa/distutils/pull/70 Patch251: 00251-change-user-install-location.patch -# 00461 # 920175020b21c0aff5edcc4c28d688b5061f591c -# Downstream only: Install wheel in test venvs when setuptools < 71 -Patch461: 00461-downstream-only-install-wheel-in-test-venvs-when-setuptools-71.patch +# 00466 # 713a1368544eddd55088d67f88a23ce31722a4cb +# Downstream only: Lower XML_COMBINED_VERSION threshold for reparse deferral +# +# RHEL 9 expat 2.5.0 has XML_SetReparseDeferralEnabled backported +# via the CVE-2023-52425 fix, but XML_COMBINED_VERSION remains 20500. +# CPython's #if XML_COMBINED_VERSION >= 20600 guards compile the setter +# as a no-op, so SetReparseDeferralEnabled silently does nothing and +# GetReparseDeferralEnabled always returns False, even though the expat +# library actually supports (and enables) reparse deferral. +# +# Lower the threshold from 20600 to 20500 so that CPython uses the +# backported function. This makes the Python API actually work on RHEL 9 +# and fixes test failures (test_reparse_deferral_disabled, +# test_flush_reparse_deferral_disabled, test_simple_xml_chunk_*). +# +# The spec file BuildRequires expat-devel >= 2.5.0-2 to ensure the +# backported function is available. +Patch466: 00466-downstream-only-lower-xml_combined_version-threshold-for-reparse-deferral.patch -# 00464 # 292acffec7a379cb6d1f3c47b9e5a2f170bbadb6 -# Enable PAC and BTI protections for aarch64 +# 00475 # d44fac01037662db286449a78c8fb819788f764c +# CVE-2025-15367 # -# Apply protection against ROP/JOP attacks for aarch64 on asm_trampoline.S -# -# The BTI flag must be applied in the assembler sources for this class -# of attacks to be mitigated on newer aarch64 processors. -# -# Upstream PR: https://github.com/python/cpython/pull/130864/files -# -# The upstream patch is incomplete but only for the case where -# frame pointers are not used on 3.13+. -# -# Since on Fedora we always compile with frame pointers the BTI/PAC -# hardware protections can be enabled without losing Perf unwinding. -Patch464: 00464-enable-pac-and-bti-protections-for-aarch64.patch +# Reject control characters in POP3 commands +Patch475: 00475-cve-2025-15367.patch -# 00466 # e10760fb955ee33d2917f8a57bb4e24d71e5341c -# Downstream only: Skip tests not working with older expat version +# 00477 # 9c62c492e7f2e3b152dbf287c08d307c3f013221 +# Raise an error when importing stdlib modules compiled for a different Python version # -# We want to run these tests in Fedora and EPEL 10, but not in EPEL 9, -# which has too old version of expat. We set the upper bound version -# in the conditionalized skip to a release available in CentOS Stream 10, -# which is tested as working. -Patch466: 00466-downstream-only-skip-tests-not-working-with-older-expat-version.patch +# This is a downstream workaround "implementing" python#137212 - +# the mechanism for the check exists in Python 3.15+, where it needs to be +# added to the standard library modules. +# In Fedora, we need it also in previous Python versions, as we experience +# segmentation fault when importing stdlib modules after update while +# Python is running. +# +# _curses, _tkinter, _tracemalloc and readline are not calling PyModuleDef_Init, +# which is modified with this patch, hence they need a +# direct call to the check function. +Patch477: 00477-raise-an-error-when-importing-stdlib-modules-compiled-for-a-different-python-version.patch # (New patches go here ^^^) # @@ -522,14 +534,13 @@ Summary: Python runtime libraries # Combined manually from https://docs.python.org/3.13/license.html # Hash of Doc/license.rst which is compared in %%prep, generated with: # $ sha256sum Doc/license.rst | cut -f1 -d" " -%global license_file_hash 62f2c9c2c75d511170eb464ad5f83b78cc1f37eb2eb49c2846c9aa6c4557ee99 +%global license_file_hash 952ac05720d7f1dcb63589b35eb0931b1442eb250230a4ad31198eddf0ad6abc # Licenses of incorporated software: # Mersenne Twister in _random C extension contains code under BSD-3-Clause # socket.getaddrinfo() and socket.getnameinfo() are BSD-3-Clause # test.support.asynchat and test.support.asyncore are MIT-CMU # http.cookies is MIT-CMU -# trace is under temporary SPDX ref: https://gitlab.com/fedora/legal/fedora-license-data/-/issues/657 -# trace: LicenseRef-Fedora-Temporary-Python-trace +# trace is HPND-SMC # uu is MIT-CMU # xmlrpc.client is MIT-CMU # test.test_epoll is MIT @@ -546,10 +557,11 @@ Summary: Python runtime libraries # mimalloc is MIT # parts of asyncio from uvloop are MIT # Python/qsbr.c is adapted from code under BSD-2-Clause +# An extract of the `Unicode Character Database` converted to an internal format is Unicode-3.0 # Bundled libb2 is not declared in the upstream document, but it's: # CC0-1.0, covered by grandfathering exception # We don't query upstream for changes, as 3.13 is the last Python version containing it. -%global libs_license Python-2.0.1 AND CC0-1.0 AND MIT AND BSD-3-Clause AND MIT-CMU AND LicenseRef-Fedora-Temporary-Python-trace AND BSD-2-Clause AND dtoa +%global libs_license Python-2.0.1 AND CC0-1.0 AND MIT AND BSD-3-Clause AND MIT-CMU AND HPND-SMC AND BSD-2-Clause AND dtoa AND Unicode-3.0 %if %{with rpmwheels} Requires: %{python_wheel_pkg_prefix}-pip-wheel >= 23.1.2 License: %{libs_license} @@ -585,6 +597,28 @@ Recommends: (%{pkgname}-tkinter%{?_isa} = %{version}-%{release} if tk%{?_isa}) # The zoneinfo module needs tzdata Requires: tzdata +# The requirement on libexpat is generated, but we need to version it. +# When built with a specific expat version, but installed with an older one, +# we sometimes get: +# ImportError: /usr/lib64/python3.X/lib-dynload/pyexpat.cpython-....so: +# undefined symbol: XML_... +# The pyexpat module has build-time checks for expat version to only use the +# available symbols. However, there is no runtime protection, so when the module +# is later installed with an older expat, it may error due to undefined symbols. +# This breaks many things, including python -m venv. +# We avoid this problem by requiring at least the same version of expat that +# was used during the build time. +# We also include release, in case pyxpat uses ABI that was backported +# (e.g. XML_SetReparseDeferralEnabled was added in c9s expat 2.5.0-2). +# Other subpackages (like -debug) also need this, but they all depend on -libs. +# Since expat 2.7.4, the library has versioned symbols and this is no longer needed, +# as the generated requirement will be in the form of libexpat.so.1(LIBEXPAT_2.7.2) etc. +%global expat_version %(LANG=C rpm -q --qf '%%{version}-%%{release}' expat.%{_target_cpu} | sed 's/.*not installed/0/') +%if v"%{expat_version}" < v"2.7.4" +Requires: expat%{?_isa} >= %{expat_version} +%endif + + %description -n %{pkgname}-libs This package contains runtime libraries for use by Python: - the majority of the Python standard library @@ -761,6 +795,9 @@ License: %{libs_license} AND Apache-2.0 AND ISC AND LGPL-2.1-only AND MPL-2.0 AN Provides: bundled(libb2) = 0.98.1 Provides: bundled(mimalloc) = 2.12 Requires: tzdata +%if v"%{expat_version}" < v"2.7.4" +Requires: expat%{?_isa} >= %{expat_version} +%endif %description -n python%{pybasever}-freethreading The provisional Free Threading (PEP 703) build of Python. @@ -1264,6 +1301,11 @@ for file in %{buildroot}%{pylibdir}/pydoc_data/topics.py $(grep --include='*.py' rm ${directory}/{__pycache__/${module}.cpython-%{pyshortver}.opt-?.pyc,${module}.py} done +%if %{without rpmwheels} +# Inject SBOM into the installed wheels (if the macro is available) +%{?python_wheel_inject_sbom:%python_wheel_inject_sbom %{buildroot}%{pylibdir}/ensurepip/_bundled/*.whl} +%endif + # ====================================================== # Checks for packaging issues # ====================================================== @@ -1776,6 +1818,42 @@ CheckPython freethreading # ====================================================== %changelog +* Wed Aug 12 2026 Miro Hrončok - 3.13.15-2 +- On EPEL 9, also supports reparse deferral in expat + +* Mon Aug 10 2026 Karolina Surma - 3.13.15-1 +- Update to Python 3.13.15 + +* Thu Jul 16 2026 Fedora Release Engineering - 3.13.14-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild + +* Thu Jun 11 2026 Karolina Surma - 3.13.14-1 +- Update to Python 3.13.14 + +* Wed Apr 08 2026 Tomáš Hrnčiar - 3.13.13-1 +- Update to 3.13.13 + +* Thu Mar 26 2026 Lumír Balhar - 3.13.12-2 +- Security fix for CVE-2026-4519 (rhbz#2449729) + +* Wed Feb 04 2026 Tomáš Hrnčiar - 3.13.12-1 +- Update to 3.13.12 + +* Sat Jan 17 2026 Fedora Release Engineering - 3.13.11-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild + +* Tue Jan 06 2026 Karolina Surma - 3.13.11-2 +- Require at least the same expat version as used during the build time + +* Fri Dec 05 2025 Miro Hrončok - 3.13.11-1 +- Update to 3.13.11 + +* Wed Dec 03 2025 Tomáš Hrnčiar - 3.13.10-1 +- Update to 3.13.10 + +* Tue Oct 14 2025 Miro Hrončok - 3.13.9-1 +- Update to Python 3.13.9 + * Tue Oct 07 2025 Karolina Surma - 3.13.8-1 - Update to Python 3.13.8 diff --git a/sources b/sources index 858b07b..d6a2436 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.13.8.tar.xz) = 8569959f24083824f6644b839c6ebd587e67fb74b3c5fed9ef44bfbbba46076e98db33a27218b2d44edd15b3b05ae85e94b71491598cbb871d7d43a9d6fa2b84 -SHA512 (Python-3.13.8.tar.xz.asc) = ea5bae3f5a909d16526b614085cd4543153800348dd82ef90945ceed19acbab8db76f7c1279e39d26b9e69608aa0f7cf1eab9a562d00fd1c9badd5b0fafc5c21 +SHA512 (Python-3.13.15.tar.xz) = b0ab766a3de0b4cfdbf0b93300d7330c734d3f7057577bb03e95e39da4cdade81993e769c7fa6dfdcc13b9d43a3b399d9cd89b5477b413662e9e691c1b7886b8 +SHA512 (Python-3.13.15.tar.xz.asc) = 662c00be95f62e80db43cf42099fef91f764ff7a4433cadf7eb19dedb5e4775adb12878d5ed2295fcea187731de186163ad9672ab72cde62a2335ca58ec1ad86