From 550562473d38af1e41d8a4328477cea9f4661b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 19 Dec 2022 16:21:06 +0000 Subject: [PATCH 01/53] No longer patch the default bytecode cache invalidation policy That is, drop patch 328. Fixes https://bugzilla.redhat.com/2133850 See also https://src.fedoraproject.org/rpms/python-rpm-macros/pull-request/154 This is part of https://fedoraproject.org/wiki/Changes/ReproducibleBuildsClampMtimes --- 00328-pyc-timestamp-invalidation-mode.patch | 54 --------------------- check-pyc-timestamps.py | 8 ++- python3.10.spec | 31 ++++++------ 3 files changed, 18 insertions(+), 75 deletions(-) delete mode 100644 00328-pyc-timestamp-invalidation-mode.patch diff --git a/00328-pyc-timestamp-invalidation-mode.patch b/00328-pyc-timestamp-invalidation-mode.patch deleted file mode 100644 index 26f09f2..0000000 --- a/00328-pyc-timestamp-invalidation-mode.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= -Date: Thu, 11 Jul 2019 13:44:13 +0200 -Subject: [PATCH] 00328: Restore pyc to TIMESTAMP invalidation mode as default - in rpmbuild - -Since Fedora 31, the $SOURCE_DATE_EPOCH is set in rpmbuild to the latest -%changelog date. This makes Python default to the CHECKED_HASH pyc -invalidation mode, bringing more reproducible builds traded for an import -performance decrease. To avoid that, we don't default to CHECKED_HASH -when $RPM_BUILD_ROOT is set (i.e. when we are building RPM packages). - -See https://src.fedoraproject.org/rpms/redhat-rpm-config/pull-request/57#comment-27426 -Downstream only: only used when building RPM packages -Ideally, we should talk to upstream and explain why we don't want this ---- - Lib/py_compile.py | 3 ++- - Lib/test/test_py_compile.py | 2 ++ - 2 files changed, 4 insertions(+), 1 deletion(-) - -diff --git a/Lib/py_compile.py b/Lib/py_compile.py -index 388614e51b..db52725016 100644 ---- a/Lib/py_compile.py -+++ b/Lib/py_compile.py -@@ -70,7 +70,8 @@ class PycInvalidationMode(enum.Enum): - - - def _get_default_invalidation_mode(): -- if os.environ.get('SOURCE_DATE_EPOCH'): -+ if (os.environ.get('SOURCE_DATE_EPOCH') and not -+ os.environ.get('RPM_BUILD_ROOT')): - return PycInvalidationMode.CHECKED_HASH - else: - return PycInvalidationMode.TIMESTAMP -diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py -index 5ed98dbff1..f30c4e43de 100644 ---- a/Lib/test/test_py_compile.py -+++ b/Lib/test/test_py_compile.py -@@ -19,6 +19,7 @@ def without_source_date_epoch(fxn): - def wrapper(*args, **kwargs): - with os_helper.EnvironmentVarGuard() as env: - env.unset('SOURCE_DATE_EPOCH') -+ env.unset('RPM_BUILD_ROOT') - return fxn(*args, **kwargs) - return wrapper - -@@ -29,6 +30,7 @@ def with_source_date_epoch(fxn): - def wrapper(*args, **kwargs): - with os_helper.EnvironmentVarGuard() as env: - env['SOURCE_DATE_EPOCH'] = '123456789' -+ env.unset('RPM_BUILD_ROOT') - return fxn(*args, **kwargs) - return wrapper - diff --git a/check-pyc-timestamps.py b/check-pyc-timestamps.py index 91af4fd..e421fca 100644 --- a/check-pyc-timestamps.py +++ b/check-pyc-timestamps.py @@ -19,11 +19,9 @@ not_compiled = [ '*/test/bad_coding.py', '*/test/bad_coding2.py', '*/test/badsyntax_*.py', - '*/lib2to3/tests/data/bom.py', - '*/lib2to3/tests/data/crlf.py', - '*/lib2to3/tests/data/different_encoding.py', - '*/lib2to3/tests/data/false_encoding.py', - '*/lib2to3/tests/data/py2_test_grammar.py', + '*/lib2to3/tests/data/*.py', + '*/lib2to3/tests/data/*/*.py', + '*/lib2to3/tests/data/*/*/*.py', '*.debug-gdb.py', ] diff --git a/python3.10.spec b/python3.10.spec index d2ee951..46b9d20 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -297,20 +297,6 @@ Patch1: 00001-rpath.patch # pypa/distutils integration: https://github.com/pypa/distutils/pull/70 Patch251: 00251-change-user-install-location.patch -# 00328 # 318e500c98f5e59eb1f23e0fcd32db69b9bd17e1 -# Restore pyc to TIMESTAMP invalidation mode as default in rpmbuild -# -# Since Fedora 31, the $SOURCE_DATE_EPOCH is set in rpmbuild to the latest -# %%changelog date. This makes Python default to the CHECKED_HASH pyc -# invalidation mode, bringing more reproducible builds traded for an import -# performance decrease. To avoid that, we don't default to CHECKED_HASH -# when $RPM_BUILD_ROOT is set (i.e. when we are building RPM packages). -# -# See https://src.fedoraproject.org/rpms/redhat-rpm-config/pull-request/57#comment-27426 -# Downstream only: only used when building RPM packages -# Ideally, we should talk to upstream and explain why we don't want this -Patch328: 00328-pyc-timestamp-invalidation-mode.patch - # 00371 # c1754d9c2750f89cb702e1b63a99201f5f7cff00 # Revert "bpo-1596321: Fix threading._shutdown() for the main thread (GH-28549) (GH-28589)" # @@ -953,15 +939,25 @@ find . -name "*~" -exec rm -f {} \; # Python CMD line options: # -s - don't add user site directory to sys.path # -B - don't write .pyc files on import +# Clamp the source mtime first, see https://fedoraproject.org/wiki/Changes/ReproducibleBuildsClampMtimes +# The clamp_source_mtime module is only guaranteed to exist on Fedoras that enabled this option: +%if 0%{?clamp_mtime_to_source_date_epoch} +LD_LIBRARY_PATH="%{buildroot}%{dynload_dir}/:%{buildroot}%{_libdir}" \ +PYTHONPATH="%{_rpmconfigdir}/redhat" \ +%{buildroot}%{_bindir}/python%{pybasever} -s -B -m clamp_source_mtime %{buildroot}%{pylibdir} +%endif # compileall CMD line options: # -f - force rebuild even if timestamps are up to date # -o - optimization levels to run compilation with # -s - part of path to left-strip from path to source file (buildroot) # -p - path to add as prefix to path to source file (/ to make it absolute) # --hardlink-dupes - hardlink different optimization level pycs together if identical (saves space) +# --invalidation-mode - we prefer the timestamp invalidation mode for performance reasons +# -x - skip test modules with SyntaxErrors (taken from the Makefile) LD_LIBRARY_PATH="%{buildroot}%{dynload_dir}/:%{buildroot}%{_libdir}" \ %{buildroot}%{_bindir}/python%{pybasever} -s -B -m compileall \ --f %{_smp_mflags} -o 0 -o 1 -o 2 -s %{buildroot} -p / %{buildroot} --hardlink-dupes || : +-f %{_smp_mflags} -o 0 -o 1 -o 2 -s %{buildroot} -p / %{buildroot} --hardlink-dupes --invalidation-mode=timestamp \ +-x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' # Turn this BRP off, it is done by compileall2 --hardlink-dupes above %global __brp_python_hardlink %{nil} @@ -1582,6 +1578,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Dec 21 2022 Miro Hrončok - 3.10.9-3 +- No longer patch the default bytecode cache invalidation policy + * Tue Dec 20 2022 Miro Hrončok - 3.10.9-2 - Supplement tox from the devel package From ad089d6d9f4a2196b4755fa58e1f00bc18bc39c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Fri, 6 Jan 2023 11:05:28 +0000 Subject: [PATCH 02/53] Opt-out from https://fedoraproject.org/wiki/Changes/fno-omit-frame-pointer See https://lists.fedoraproject.org/archives/list/python-devel@lists.fedoraproject.org/thread/6TQYCHMX4FZLF27U5BCEC7IFV6XNBKJP/ for rationale, namely https://lists.fedoraproject.org/archives/list/python-devel@lists.fedoraproject.org/message/ZVDEXGPU6JQFXB3XHYZ4IXVQNNR3YM3V/ Summary: Python is currently slower with frame pointers due to a slowdown in _PyEval_EvalFrameDefault, but we expect this to be solved in Python 3.12. Tracking bugzilla: https://bugzilla.redhat.com/2158729 This change does not require a release bump. It is only needed to be here to prevent the next builds from including frame pointers. --- python3.10.spec | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python3.10.spec b/python3.10.spec index 46b9d20..007de02 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -175,6 +175,12 @@ Obsoletes: python%{pybasever}%{?1:-%{1}}\ %define unversioned_obsoletes_of_python3_X_if_main() %{nil} %endif +# Opt-out from https://fedoraproject.org/wiki/Changes/fno-omit-frame-pointer +# Python is slower with frame pointers, but we expect to remove this in Python 3.12+ +# See https://lists.fedoraproject.org/archives/list/python-devel@lists.fedoraproject.org/thread/6TQYCHMX4FZLF27U5BCEC7IFV6XNBKJP/ +# Tracking bugzilla: https://bugzilla.redhat.com/2158729 +%undefine _include_frame_pointers + # ======================= # Build-time requirements # ======================= From 70c29816959f604b9283d1224f0e1003669761da Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 20 Jan 2023 18:45:00 +0000 Subject: [PATCH 03/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 007de02..d63f6b8 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 4%{?dist} License: Python-2.0.1 @@ -1584,6 +1584,9 @@ CheckPython optimized # ====================================================== %changelog +* Fri Jan 20 2023 Fedora Release Engineering - 3.10.9-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + * Wed Dec 21 2022 Miro Hrončok - 3.10.9-3 - No longer patch the default bytecode cache invalidation policy From f7c068e6b95b5ee7e50db54d86b6dca91da98369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Wed, 8 Feb 2023 16:42:47 +0100 Subject: [PATCH 04/53] Update to 3.10.10 --- python3.10.spec | 9 +++++++-- sources | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index d63f6b8..7c75a28 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.9 +%global general_version %{pybasever}.10 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 4%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -1427,6 +1427,7 @@ CheckPython optimized %{dynload_dir}/_ctypes_test.%{SOABI_optimized}.so %{dynload_dir}/_testbuffer.%{SOABI_optimized}.so %{dynload_dir}/_testcapi.%{SOABI_optimized}.so +%{dynload_dir}/_testclinic.%{SOABI_optimized}.so %{dynload_dir}/_testimportmultiple.%{SOABI_optimized}.so %{dynload_dir}/_testinternalcapi.%{SOABI_optimized}.so %{dynload_dir}/_testmultiphase.%{SOABI_optimized}.so @@ -1554,6 +1555,7 @@ CheckPython optimized %{dynload_dir}/_ctypes_test.%{SOABI_debug}.so %{dynload_dir}/_testbuffer.%{SOABI_debug}.so %{dynload_dir}/_testcapi.%{SOABI_debug}.so +%{dynload_dir}/_testclinic.%{SOABI_debug}.so %{dynload_dir}/_testimportmultiple.%{SOABI_debug}.so %{dynload_dir}/_testinternalcapi.%{SOABI_debug}.so %{dynload_dir}/_testmultiphase.%{SOABI_debug}.so @@ -1584,6 +1586,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Feb 08 2023 Tomáš Hrnčiar - 3.10.10-1 +- Update to 3.10.10 + * Fri Jan 20 2023 Fedora Release Engineering - 3.10.9-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild diff --git a/sources b/sources index f7775cf..ee15d48 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.9.tar.xz) = d66ea8adeb6dc4951e612175f8838b3092967ff275b7a3470f2d86f470036aa2221e722c3144d90bcd230b88efd53dde204213f72f703e524e4b833e2ccc68e2 -SHA512 (Python-3.10.9.tar.xz.asc) = 525e166ede6836086de814c26fc880f41eaf1ed4bff6118f00342e42f7ab1c47148447ced1b565e146d3125fa06b5cd6b394a256bc61096766b26e07f18bbf10 +SHA512 (Python-3.10.10.tar.xz) = f0aee65970a68287b34c4eafcf35c6fa09c81ba234ac356db16fbbc6c36417e4ac67071e616d118f5e192d541d7f177dcab5585b9780e842f656c09e01c37ced +SHA512 (Python-3.10.10.tar.xz.asc) = 591746d74c6123bf36c763b6e8e1de1554f02eeff30c855623ef0f12d3864d5573eb5efe96d6e142f24627c77b90738ada3456df4ad59bddcb008658f2ca8af9 From 1797d6370092f58c49282274e8257faf0e919249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Wed, 5 Apr 2023 14:36:49 +0200 Subject: [PATCH 05/53] Update to 3.10.11 --- python3.10.spec | 7 +++++-- sources | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index 7c75a28..e1f4bdb 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,7 +13,7 @@ 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}.10 +%global general_version %{pybasever}.11 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} @@ -67,7 +67,7 @@ License: Python-2.0.1 # If the rpmwheels condition is disabled, we use the bundled wheel packages # from Python with the versions below. # This needs to be manually updated when we update Python. -%global pip_version 22.3.1 +%global pip_version 23.0.1 %global setuptools_version 65.5.0 # Expensive optimizations (mainly, profile-guided optimizations) @@ -1586,6 +1586,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Apr 05 2023 Tomáš Hrnčiar - 3.10.11-1 +- Update to 3.10.11 + * Wed Feb 08 2023 Tomáš Hrnčiar - 3.10.10-1 - Update to 3.10.10 diff --git a/sources b/sources index ee15d48..5418fac 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.10.tar.xz) = f0aee65970a68287b34c4eafcf35c6fa09c81ba234ac356db16fbbc6c36417e4ac67071e616d118f5e192d541d7f177dcab5585b9780e842f656c09e01c37ced -SHA512 (Python-3.10.10.tar.xz.asc) = 591746d74c6123bf36c763b6e8e1de1554f02eeff30c855623ef0f12d3864d5573eb5efe96d6e142f24627c77b90738ada3456df4ad59bddcb008658f2ca8af9 +SHA512 (Python-3.10.11.tar.xz) = fa113b4b635d271a1412999587ec64654d337db263851a6a9d88b3cab4ed66dba76fe03e65c4d341f0a83fd8182d35e245bfd9827465d7aebcb4deb71af4d047 +SHA512 (Python-3.10.11.tar.xz.asc) = edddacc81d4671851cd2a52853cabc53314fa226dac77c2248ee61cf7d58dd2f59301b7729b2ff9f5717b84268140c3bef30e358b80718c6c84b308bd560bcd8 From db3fe4ad656043bd64e7154a6aa0377f20c8e1e1 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Mon, 29 May 2023 17:16:39 +0200 Subject: [PATCH 06/53] Fix for CVE-2023-24329 Resolves: rhbz#2174010 --- 00399-cve-2023-24329.patch | 223 +++++++++++++++++++++++++++++++++++++ python3.10.spec | 18 ++- 2 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 00399-cve-2023-24329.patch diff --git a/00399-cve-2023-24329.patch b/00399-cve-2023-24329.patch new file mode 100644 index 0000000..c94db0a --- /dev/null +++ b/00399-cve-2023-24329.patch @@ -0,0 +1,223 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Wed, 17 May 2023 16:06:06 -0700 +Subject: [PATCH] 00399: CVE-2023-24329 + +gh-102153: Start stripping C0 control and space chars in `urlsplit` (GH-102508) + +`urllib.parse.urlsplit` has already been respecting the WHATWG spec a bit GH-25595. + +This adds more sanitizing to respect the "Remove any leading C0 control or space from input" [rule](https://url.spec.whatwg.org/GH-url-parsing:~:text=Remove%20any%20leading%20and%20trailing%20C0%20control%20or%20space%20from%20input.) in response to [CVE-2023-24329](https://nvd.nist.gov/vuln/detail/CVE-2023-24329). + +--------- + +(cherry picked from commit 2f630e1ce18ad2e07428296532a68b11dc66ad10) +(cherry picked from commit 610cc0ab1b760b2abaac92bd256b96191c46b941) + +Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> +Co-authored-by: Illia Volochii +Co-authored-by: Gregory P. Smith [Google] +--- + Doc/library/urllib.parse.rst | 38 +++++++++++- + Lib/test/test_urlparse.py | 61 ++++++++++++++++++- + Lib/urllib/parse.py | 12 ++++ + ...-03-07-20-59-17.gh-issue-102153.14CLSZ.rst | 3 + + 4 files changed, 111 insertions(+), 3 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst + +diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst +index 96b3965107..1e85602951 100644 +--- a/Doc/library/urllib.parse.rst ++++ b/Doc/library/urllib.parse.rst +@@ -159,6 +159,10 @@ or on combining URL components into a URL string. + ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', + params='', query='', fragment='') + ++ .. warning:: ++ ++ :func:`urlparse` does not perform validation. See :ref:`URL parsing ++ security ` for details. + + .. versionchanged:: 3.2 + Added IPv6 URL parsing capabilities. +@@ -324,8 +328,14 @@ or on combining URL components into a URL string. + ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is + decomposed before parsing, no error will be raised. + +- Following the `WHATWG spec`_ that updates RFC 3986, ASCII newline +- ``\n``, ``\r`` and tab ``\t`` characters are stripped from the URL. ++ Following some of the `WHATWG spec`_ that updates RFC 3986, leading C0 ++ control and space characters are stripped from the URL. ``\n``, ++ ``\r`` and tab ``\t`` characters are removed from the URL at any position. ++ ++ .. warning:: ++ ++ :func:`urlsplit` does not perform validation. See :ref:`URL parsing ++ security ` for details. + + .. versionchanged:: 3.6 + Out-of-range port numbers now raise :exc:`ValueError`, instead of +@@ -338,6 +348,9 @@ or on combining URL components into a URL string. + .. versionchanged:: 3.10 + ASCII newline and tab characters are stripped from the URL. + ++ .. versionchanged:: 3.10.12 ++ Leading WHATWG C0 control and space characters are stripped from the URL. ++ + .. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser + + .. function:: urlunsplit(parts) +@@ -414,6 +427,27 @@ or on combining URL components into a URL string. + or ``scheme://host/path``). If *url* is not a wrapped URL, it is returned + without changes. + ++.. _url-parsing-security: ++ ++URL parsing security ++-------------------- ++ ++The :func:`urlsplit` and :func:`urlparse` APIs do not perform **validation** of ++inputs. They may not raise errors on inputs that other applications consider ++invalid. They may also succeed on some inputs that might not be considered ++URLs elsewhere. Their purpose is for practical functionality rather than ++purity. ++ ++Instead of raising an exception on unusual input, they may instead return some ++component parts as empty strings. Or components may contain more than perhaps ++they should. ++ ++We recommend that users of these APIs where the values may be used anywhere ++with security implications code defensively. Do some verification within your ++code before trusting a returned component part. Does that ``scheme`` make ++sense? Is that a sensible ``path``? Is there anything strange about that ++``hostname``? etc. ++ + .. _parsing-ascii-encoded-bytes: + + Parsing ASCII Encoded Bytes +diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py +index ca37c3c403..b0aed37de7 100644 +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -649,6 +649,65 @@ def test_urlsplit_remove_unsafe_bytes(self): + self.assertEqual(p.scheme, "http") + self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment") + ++ def test_urlsplit_strip_url(self): ++ noise = bytes(range(0, 0x20 + 1)) ++ base_url = "http://User:Pass@www.python.org:080/doc/?query=yes#frag" ++ ++ url = noise.decode("utf-8") + base_url ++ p = urllib.parse.urlsplit(url) ++ self.assertEqual(p.scheme, "http") ++ self.assertEqual(p.netloc, "User:Pass@www.python.org:080") ++ self.assertEqual(p.path, "/doc/") ++ self.assertEqual(p.query, "query=yes") ++ self.assertEqual(p.fragment, "frag") ++ self.assertEqual(p.username, "User") ++ self.assertEqual(p.password, "Pass") ++ self.assertEqual(p.hostname, "www.python.org") ++ self.assertEqual(p.port, 80) ++ self.assertEqual(p.geturl(), base_url) ++ ++ url = noise + base_url.encode("utf-8") ++ p = urllib.parse.urlsplit(url) ++ self.assertEqual(p.scheme, b"http") ++ self.assertEqual(p.netloc, b"User:Pass@www.python.org:080") ++ self.assertEqual(p.path, b"/doc/") ++ self.assertEqual(p.query, b"query=yes") ++ self.assertEqual(p.fragment, b"frag") ++ self.assertEqual(p.username, b"User") ++ self.assertEqual(p.password, b"Pass") ++ self.assertEqual(p.hostname, b"www.python.org") ++ self.assertEqual(p.port, 80) ++ self.assertEqual(p.geturl(), base_url.encode("utf-8")) ++ ++ # Test that trailing space is preserved as some applications rely on ++ # this within query strings. ++ query_spaces_url = "https://www.python.org:88/doc/?query= " ++ p = urllib.parse.urlsplit(noise.decode("utf-8") + query_spaces_url) ++ self.assertEqual(p.scheme, "https") ++ self.assertEqual(p.netloc, "www.python.org:88") ++ self.assertEqual(p.path, "/doc/") ++ self.assertEqual(p.query, "query= ") ++ self.assertEqual(p.port, 88) ++ self.assertEqual(p.geturl(), query_spaces_url) ++ ++ p = urllib.parse.urlsplit("www.pypi.org ") ++ # That "hostname" gets considered a "path" due to the ++ # trailing space and our existing logic... YUCK... ++ # and re-assembles via geturl aka unurlsplit into the original. ++ # django.core.validators.URLValidator (at least through v3.2) relies on ++ # this, for better or worse, to catch it in a ValidationError via its ++ # regular expressions. ++ # Here we test the basic round trip concept of such a trailing space. ++ self.assertEqual(urllib.parse.urlunsplit(p), "www.pypi.org ") ++ ++ # with scheme as cache-key ++ url = "//www.python.org/" ++ scheme = noise.decode("utf-8") + "https" + noise.decode("utf-8") ++ for _ in range(2): ++ p = urllib.parse.urlsplit(url, scheme=scheme) ++ self.assertEqual(p.scheme, "https") ++ self.assertEqual(p.geturl(), "https://www.python.org/") ++ + def test_attributes_bad_port(self): + """Check handling of invalid ports.""" + for bytes in (False, True): +@@ -656,7 +715,7 @@ def test_attributes_bad_port(self): + for port in ("foo", "1.5", "-1", "0x10", "-0", "1_1", " 1", "1 ", "६"): + with self.subTest(bytes=bytes, parse=parse, port=port): + netloc = "www.example.net:" + port +- url = "http://" + netloc ++ url = "http://" + netloc + "/" + if bytes: + if netloc.isascii() and port.isascii(): + netloc = netloc.encode("ascii") +diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py +index 26ddf30748..0ab2023843 100644 +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -25,6 +25,10 @@ + scenarios for parsing, and for backward compatibility purposes, some + parsing quirks from older RFCs are retained. The testcases in + test_urlparse.py provides a good indicator of parsing behavior. ++ ++The WHATWG URL Parser spec should also be considered. We are not compliant with ++it either due to existing user code API behavior expectations (Hyrum's Law). ++It serves as a useful guide when making changes. + """ + + import re +@@ -78,6 +82,10 @@ + '0123456789' + '+-.') + ++# Leading and trailing C0 control and space to be stripped per WHATWG spec. ++# == "".join([chr(i) for i in range(0, 0x20 + 1)]) ++_WHATWG_C0_CONTROL_OR_SPACE = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ' ++ + # Unsafe bytes to be removed per WHATWG spec + _UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n'] + +@@ -455,6 +463,10 @@ def urlsplit(url, scheme='', allow_fragments=True): + """ + + url, scheme, _coerce_result = _coerce_args(url, scheme) ++ # Only lstrip url as some applications rely on preserving trailing space. ++ # (https://url.spec.whatwg.org/#concept-basic-url-parser would strip both) ++ url = url.lstrip(_WHATWG_C0_CONTROL_OR_SPACE) ++ scheme = scheme.strip(_WHATWG_C0_CONTROL_OR_SPACE) + + for b in _UNSAFE_URL_BYTES_TO_REMOVE: + url = url.replace(b, "") +diff --git a/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst b/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst +new file mode 100644 +index 0000000000..e57ac4ed3a +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst +@@ -0,0 +1,3 @@ ++:func:`urllib.parse.urlsplit` now strips leading C0 control and space ++characters following the specification for URLs defined by WHATWG in ++response to CVE-2023-24329. Patch by Illia Volochii. diff --git a/python3.10.spec b/python3.10.spec index e1f4bdb..e548823 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -314,6 +314,18 @@ Patch251: 00251-change-user-install-location.patch # https://github.com/GrahamDumpleton/mod_wsgi/issues/730 Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch +# 00399 # 9d45c55d8a6f96654b56fba6fbded29745e6fc29 +# CVE-2023-24329 +# +# gh-102153: Start stripping C0 control and space chars in `urlsplit` (GH-102508) +# +# `urllib.parse.urlsplit` has already been respecting the WHATWG spec a bit GH-25595. +# +# This adds more sanitizing to respect the "Remove any leading C0 control or space from input" [rule](https://url.spec.whatwg.org/GH-url-parsing:~:text=Remove%%20any%%20leading%%20and%%20trailing%%20C0%%20control%%20or%%20space%%20from%%20input.) in response to [CVE-2023-24329](https://nvd.nist.gov/vuln/detail/CVE-2023-24329). +# +# --------- +Patch399: 00399-cve-2023-24329.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1586,6 +1598,10 @@ CheckPython optimized # ====================================================== %changelog +* Mon May 29 2023 Charalampos Stratakis - 3.10.11-2 +- Fix for CVE-2023-24329 +Resolves: rhbz#2174010 + * Wed Apr 05 2023 Tomáš Hrnčiar - 3.10.11-1 - Update to 3.10.11 From 718dd7e9a5a304d0d9d8cbe495deaf60a91c8b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Thu, 8 Jun 2023 09:33:27 +0200 Subject: [PATCH 07/53] Update to 3.10.12 --- 00399-cve-2023-24329.patch | 223 ------------------------------------- python3.10.spec | 19 +--- sources | 4 +- 3 files changed, 7 insertions(+), 239 deletions(-) delete mode 100644 00399-cve-2023-24329.patch diff --git a/00399-cve-2023-24329.patch b/00399-cve-2023-24329.patch deleted file mode 100644 index c94db0a..0000000 --- a/00399-cve-2023-24329.patch +++ /dev/null @@ -1,223 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Wed, 17 May 2023 16:06:06 -0700 -Subject: [PATCH] 00399: CVE-2023-24329 - -gh-102153: Start stripping C0 control and space chars in `urlsplit` (GH-102508) - -`urllib.parse.urlsplit` has already been respecting the WHATWG spec a bit GH-25595. - -This adds more sanitizing to respect the "Remove any leading C0 control or space from input" [rule](https://url.spec.whatwg.org/GH-url-parsing:~:text=Remove%20any%20leading%20and%20trailing%20C0%20control%20or%20space%20from%20input.) in response to [CVE-2023-24329](https://nvd.nist.gov/vuln/detail/CVE-2023-24329). - ---------- - -(cherry picked from commit 2f630e1ce18ad2e07428296532a68b11dc66ad10) -(cherry picked from commit 610cc0ab1b760b2abaac92bd256b96191c46b941) - -Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> -Co-authored-by: Illia Volochii -Co-authored-by: Gregory P. Smith [Google] ---- - Doc/library/urllib.parse.rst | 38 +++++++++++- - Lib/test/test_urlparse.py | 61 ++++++++++++++++++- - Lib/urllib/parse.py | 12 ++++ - ...-03-07-20-59-17.gh-issue-102153.14CLSZ.rst | 3 + - 4 files changed, 111 insertions(+), 3 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst - -diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst -index 96b3965107..1e85602951 100644 ---- a/Doc/library/urllib.parse.rst -+++ b/Doc/library/urllib.parse.rst -@@ -159,6 +159,10 @@ or on combining URL components into a URL string. - ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', - params='', query='', fragment='') - -+ .. warning:: -+ -+ :func:`urlparse` does not perform validation. See :ref:`URL parsing -+ security ` for details. - - .. versionchanged:: 3.2 - Added IPv6 URL parsing capabilities. -@@ -324,8 +328,14 @@ or on combining URL components into a URL string. - ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is - decomposed before parsing, no error will be raised. - -- Following the `WHATWG spec`_ that updates RFC 3986, ASCII newline -- ``\n``, ``\r`` and tab ``\t`` characters are stripped from the URL. -+ Following some of the `WHATWG spec`_ that updates RFC 3986, leading C0 -+ control and space characters are stripped from the URL. ``\n``, -+ ``\r`` and tab ``\t`` characters are removed from the URL at any position. -+ -+ .. warning:: -+ -+ :func:`urlsplit` does not perform validation. See :ref:`URL parsing -+ security ` for details. - - .. versionchanged:: 3.6 - Out-of-range port numbers now raise :exc:`ValueError`, instead of -@@ -338,6 +348,9 @@ or on combining URL components into a URL string. - .. versionchanged:: 3.10 - ASCII newline and tab characters are stripped from the URL. - -+ .. versionchanged:: 3.10.12 -+ Leading WHATWG C0 control and space characters are stripped from the URL. -+ - .. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser - - .. function:: urlunsplit(parts) -@@ -414,6 +427,27 @@ or on combining URL components into a URL string. - or ``scheme://host/path``). If *url* is not a wrapped URL, it is returned - without changes. - -+.. _url-parsing-security: -+ -+URL parsing security -+-------------------- -+ -+The :func:`urlsplit` and :func:`urlparse` APIs do not perform **validation** of -+inputs. They may not raise errors on inputs that other applications consider -+invalid. They may also succeed on some inputs that might not be considered -+URLs elsewhere. Their purpose is for practical functionality rather than -+purity. -+ -+Instead of raising an exception on unusual input, they may instead return some -+component parts as empty strings. Or components may contain more than perhaps -+they should. -+ -+We recommend that users of these APIs where the values may be used anywhere -+with security implications code defensively. Do some verification within your -+code before trusting a returned component part. Does that ``scheme`` make -+sense? Is that a sensible ``path``? Is there anything strange about that -+``hostname``? etc. -+ - .. _parsing-ascii-encoded-bytes: - - Parsing ASCII Encoded Bytes -diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py -index ca37c3c403..b0aed37de7 100644 ---- a/Lib/test/test_urlparse.py -+++ b/Lib/test/test_urlparse.py -@@ -649,6 +649,65 @@ def test_urlsplit_remove_unsafe_bytes(self): - self.assertEqual(p.scheme, "http") - self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment") - -+ def test_urlsplit_strip_url(self): -+ noise = bytes(range(0, 0x20 + 1)) -+ base_url = "http://User:Pass@www.python.org:080/doc/?query=yes#frag" -+ -+ url = noise.decode("utf-8") + base_url -+ p = urllib.parse.urlsplit(url) -+ self.assertEqual(p.scheme, "http") -+ self.assertEqual(p.netloc, "User:Pass@www.python.org:080") -+ self.assertEqual(p.path, "/doc/") -+ self.assertEqual(p.query, "query=yes") -+ self.assertEqual(p.fragment, "frag") -+ self.assertEqual(p.username, "User") -+ self.assertEqual(p.password, "Pass") -+ self.assertEqual(p.hostname, "www.python.org") -+ self.assertEqual(p.port, 80) -+ self.assertEqual(p.geturl(), base_url) -+ -+ url = noise + base_url.encode("utf-8") -+ p = urllib.parse.urlsplit(url) -+ self.assertEqual(p.scheme, b"http") -+ self.assertEqual(p.netloc, b"User:Pass@www.python.org:080") -+ self.assertEqual(p.path, b"/doc/") -+ self.assertEqual(p.query, b"query=yes") -+ self.assertEqual(p.fragment, b"frag") -+ self.assertEqual(p.username, b"User") -+ self.assertEqual(p.password, b"Pass") -+ self.assertEqual(p.hostname, b"www.python.org") -+ self.assertEqual(p.port, 80) -+ self.assertEqual(p.geturl(), base_url.encode("utf-8")) -+ -+ # Test that trailing space is preserved as some applications rely on -+ # this within query strings. -+ query_spaces_url = "https://www.python.org:88/doc/?query= " -+ p = urllib.parse.urlsplit(noise.decode("utf-8") + query_spaces_url) -+ self.assertEqual(p.scheme, "https") -+ self.assertEqual(p.netloc, "www.python.org:88") -+ self.assertEqual(p.path, "/doc/") -+ self.assertEqual(p.query, "query= ") -+ self.assertEqual(p.port, 88) -+ self.assertEqual(p.geturl(), query_spaces_url) -+ -+ p = urllib.parse.urlsplit("www.pypi.org ") -+ # That "hostname" gets considered a "path" due to the -+ # trailing space and our existing logic... YUCK... -+ # and re-assembles via geturl aka unurlsplit into the original. -+ # django.core.validators.URLValidator (at least through v3.2) relies on -+ # this, for better or worse, to catch it in a ValidationError via its -+ # regular expressions. -+ # Here we test the basic round trip concept of such a trailing space. -+ self.assertEqual(urllib.parse.urlunsplit(p), "www.pypi.org ") -+ -+ # with scheme as cache-key -+ url = "//www.python.org/" -+ scheme = noise.decode("utf-8") + "https" + noise.decode("utf-8") -+ for _ in range(2): -+ p = urllib.parse.urlsplit(url, scheme=scheme) -+ self.assertEqual(p.scheme, "https") -+ self.assertEqual(p.geturl(), "https://www.python.org/") -+ - def test_attributes_bad_port(self): - """Check handling of invalid ports.""" - for bytes in (False, True): -@@ -656,7 +715,7 @@ def test_attributes_bad_port(self): - for port in ("foo", "1.5", "-1", "0x10", "-0", "1_1", " 1", "1 ", "६"): - with self.subTest(bytes=bytes, parse=parse, port=port): - netloc = "www.example.net:" + port -- url = "http://" + netloc -+ url = "http://" + netloc + "/" - if bytes: - if netloc.isascii() and port.isascii(): - netloc = netloc.encode("ascii") -diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py -index 26ddf30748..0ab2023843 100644 ---- a/Lib/urllib/parse.py -+++ b/Lib/urllib/parse.py -@@ -25,6 +25,10 @@ - scenarios for parsing, and for backward compatibility purposes, some - parsing quirks from older RFCs are retained. The testcases in - test_urlparse.py provides a good indicator of parsing behavior. -+ -+The WHATWG URL Parser spec should also be considered. We are not compliant with -+it either due to existing user code API behavior expectations (Hyrum's Law). -+It serves as a useful guide when making changes. - """ - - import re -@@ -78,6 +82,10 @@ - '0123456789' - '+-.') - -+# Leading and trailing C0 control and space to be stripped per WHATWG spec. -+# == "".join([chr(i) for i in range(0, 0x20 + 1)]) -+_WHATWG_C0_CONTROL_OR_SPACE = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ' -+ - # Unsafe bytes to be removed per WHATWG spec - _UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n'] - -@@ -455,6 +463,10 @@ def urlsplit(url, scheme='', allow_fragments=True): - """ - - url, scheme, _coerce_result = _coerce_args(url, scheme) -+ # Only lstrip url as some applications rely on preserving trailing space. -+ # (https://url.spec.whatwg.org/#concept-basic-url-parser would strip both) -+ url = url.lstrip(_WHATWG_C0_CONTROL_OR_SPACE) -+ scheme = scheme.strip(_WHATWG_C0_CONTROL_OR_SPACE) - - for b in _UNSAFE_URL_BYTES_TO_REMOVE: - url = url.replace(b, "") -diff --git a/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst b/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst -new file mode 100644 -index 0000000000..e57ac4ed3a ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst -@@ -0,0 +1,3 @@ -+:func:`urllib.parse.urlsplit` now strips leading C0 control and space -+characters following the specification for URLs defined by WHATWG in -+response to CVE-2023-24329. Patch by Illia Volochii. diff --git a/python3.10.spec b/python3.10.spec index e548823..196265b 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.11 +%global general_version %{pybasever}.12 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -314,18 +314,6 @@ Patch251: 00251-change-user-install-location.patch # https://github.com/GrahamDumpleton/mod_wsgi/issues/730 Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch -# 00399 # 9d45c55d8a6f96654b56fba6fbded29745e6fc29 -# CVE-2023-24329 -# -# gh-102153: Start stripping C0 control and space chars in `urlsplit` (GH-102508) -# -# `urllib.parse.urlsplit` has already been respecting the WHATWG spec a bit GH-25595. -# -# This adds more sanitizing to respect the "Remove any leading C0 control or space from input" [rule](https://url.spec.whatwg.org/GH-url-parsing:~:text=Remove%%20any%%20leading%%20and%%20trailing%%20C0%%20control%%20or%%20space%%20from%%20input.) in response to [CVE-2023-24329](https://nvd.nist.gov/vuln/detail/CVE-2023-24329). -# -# --------- -Patch399: 00399-cve-2023-24329.patch - # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1598,6 +1586,9 @@ CheckPython optimized # ====================================================== %changelog +* Thu Jun 08 2023 Tomáš Hrnčiar - 3.10.12-1 +- Update to 3.10.12 + * Mon May 29 2023 Charalampos Stratakis - 3.10.11-2 - Fix for CVE-2023-24329 Resolves: rhbz#2174010 diff --git a/sources b/sources index 5418fac..cec50a6 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.11.tar.xz) = fa113b4b635d271a1412999587ec64654d337db263851a6a9d88b3cab4ed66dba76fe03e65c4d341f0a83fd8182d35e245bfd9827465d7aebcb4deb71af4d047 -SHA512 (Python-3.10.11.tar.xz.asc) = edddacc81d4671851cd2a52853cabc53314fa226dac77c2248ee61cf7d58dd2f59301b7729b2ff9f5717b84268140c3bef30e358b80718c6c84b308bd560bcd8 +SHA512 (Python-3.10.12.tar.xz) = 5ea018e71bfe7872e02eaf8aef56d5583c0880e4ce5fbbdf8ea76da20c2e94ac6a3ba8badb4b7d1bc21853402a3b63541b04181737417b1626e786b696595cf5 +SHA512 (Python-3.10.12.tar.xz.asc) = d5f1c98d8509e078bf7d7205f9fe25d028178fb7940cff406761c14a2689a82425f6bfe9b58b2aa6e9d4fee53626fb56658e56bd54028245de827dc5ebc1d7d8 From 1a8b07885e88d8a329da3a7afaf44395340fb978 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 21 Jul 2023 15:41:58 +0000 Subject: [PATCH 08/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 196265b..19f0e1f 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -1586,6 +1586,9 @@ CheckPython optimized # ====================================================== %changelog +* Fri Jul 21 2023 Fedora Release Engineering - 3.10.12-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + * Thu Jun 08 2023 Tomáš Hrnčiar - 3.10.12-1 - Update to 3.10.12 From 53f7dd0a425578094964f1d20d2c61c4d5f99547 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Mon, 22 May 2023 19:23:07 +0200 Subject: [PATCH 09/53] Remove extra distro-applied CFLAGS passed to user-built C extensions Only -fexceptions and -fcf-protection are preserved for binary compatibility with user-built python C extension. https://fedoraproject.org/wiki/Changes/Python_Extension_Flags_Reduction --- python3.10.spec | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index 19f0e1f..4a77f6b 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -674,14 +674,15 @@ topdir=$(pwd) # Standard library built here will still use the %%build_...flags, # Fedora packages utilizing %%py3_build will use them as well # https://fedoraproject.org/wiki/Changes/Python_Extension_Flags -export CFLAGS="%{extension_cflags} -D_GNU_SOURCE -fPIC -fwrapv" +# https://fedoraproject.org/wiki/Changes/Python_Extension_Flags_Reduction +export CFLAGS="%{extension_cflags}" export CFLAGS_NODIST="%{build_cflags} -D_GNU_SOURCE -fPIC -fwrapv" -export CXXFLAGS="%{extension_cxxflags} -D_GNU_SOURCE -fPIC -fwrapv" +export CXXFLAGS="%{extension_cxxflags}" export CPPFLAGS="$(pkg-config --cflags-only-I libffi)" -export OPT="%{extension_cflags} -D_GNU_SOURCE -fPIC -fwrapv" +export OPT="%{extension_cflags}" export LINKCC="gcc" export CFLAGS="$CFLAGS $(pkg-config --cflags openssl)" -export LDFLAGS="%{extension_ldflags} -g $(pkg-config --libs-only-L openssl)" +export LDFLAGS="%{extension_ldflags} $(pkg-config --libs-only-L openssl)" export LDFLAGS_NODIST="%{build_ldflags} -g $(pkg-config --libs-only-L openssl)" # We can build several different configurations of Python: regular and debug. @@ -1586,6 +1587,10 @@ CheckPython optimized # ====================================================== %changelog +* Wed Aug 02 2023 Charalampos Stratakis - 3.10.12-3 +- Remove extra distro-applied CFLAGS passed to user built C extensions +- https://fedoraproject.org/wiki/Changes/Python_Extension_Flags_Reduction + * Fri Jul 21 2023 Fedora Release Engineering - 3.10.12-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild From f9e0b9baeacc90c07c38ba7e311d8704def8e555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Mon, 28 Aug 2023 15:35:01 +0200 Subject: [PATCH 10/53] Update to 3.10.13 --- python3.10.spec | 7 +++++-- sources | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index 4a77f6b..11b235e 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.12 +%global general_version %{pybasever}.13 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -1587,6 +1587,9 @@ CheckPython optimized # ====================================================== %changelog +* Mon Aug 28 2023 Tomáš Hrnčiar - 3.10.13-1 +- Update to 3.10.13 + * Wed Aug 02 2023 Charalampos Stratakis - 3.10.12-3 - Remove extra distro-applied CFLAGS passed to user built C extensions - https://fedoraproject.org/wiki/Changes/Python_Extension_Flags_Reduction diff --git a/sources b/sources index cec50a6..b4b0734 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.12.tar.xz) = 5ea018e71bfe7872e02eaf8aef56d5583c0880e4ce5fbbdf8ea76da20c2e94ac6a3ba8badb4b7d1bc21853402a3b63541b04181737417b1626e786b696595cf5 -SHA512 (Python-3.10.12.tar.xz.asc) = d5f1c98d8509e078bf7d7205f9fe25d028178fb7940cff406761c14a2689a82425f6bfe9b58b2aa6e9d4fee53626fb56658e56bd54028245de827dc5ebc1d7d8 +SHA512 (Python-3.10.13.tar.xz) = 7579772e501486b2b07f78142082dee1e99c7643640098860ac0cf2ca87daf7588b0c00b1db1960146b37f56a6ed98fd08297c25c9a19b612cf6e6a258984da8 +SHA512 (Python-3.10.13.tar.xz.asc) = 3083e66f8d26128302dc77a0c6ba3bfefc4229928a1bbd06460f2fec2421188bd30d493e3bce138cde1fed1df206e1dda04912b9f43a0b81229f1e69135e3a7f From 034e3e58be9a5246404c09b6c871fc039004ee9f Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Mon, 18 Dec 2023 13:15:37 +0100 Subject: [PATCH 11/53] Security fix for CVE-2023-27043 (rhbz#2196187) --- ...-addresses-in-email-parseaddr-111116.patch | 500 ++++++++++++++++++ python3.10.spec | 14 +- 2 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch diff --git a/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch b/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch new file mode 100644 index 0000000..7d2a813 --- /dev/null +++ b/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch @@ -0,0 +1,500 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Victor Stinner +Date: Fri, 15 Dec 2023 16:10:40 +0100 +Subject: [PATCH] 00415: [CVE-2023-27043] gh-102988: Reject malformed addresses + in email.parseaddr() (#111116) + +Detect email address parsing errors and return empty tuple to +indicate the parsing error (old API). Add an optional 'strict' +parameter to getaddresses() and parseaddr() functions. Patch by +Thomas Dwyer. + +Co-Authored-By: Thomas Dwyer +--- + Doc/library/email.utils.rst | 19 +- + Lib/email/utils.py | 151 ++++++++++++- + Lib/test/test_email/test_email.py | 204 +++++++++++++++++- + ...-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 + + 4 files changed, 361 insertions(+), 21 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst + +diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst +index 0e266b6a45..6723dc4f13 100644 +--- a/Doc/library/email.utils.rst ++++ b/Doc/library/email.utils.rst +@@ -60,13 +60,18 @@ of the new API. + begins with angle brackets, they are stripped off. + + +-.. function:: parseaddr(address) ++.. function:: parseaddr(address, *, strict=True) + + Parse address -- which should be the value of some address-containing field such + as :mailheader:`To` or :mailheader:`Cc` -- into its constituent *realname* and + *email address* parts. Returns a tuple of that information, unless the parse + fails, in which case a 2-tuple of ``('', '')`` is returned. + ++ If *strict* is true, use a strict parser which rejects malformed inputs. ++ ++ .. versionchanged:: 3.13 ++ Add *strict* optional parameter and reject malformed inputs by default. ++ + + .. function:: formataddr(pair, charset='utf-8') + +@@ -84,12 +89,15 @@ of the new API. + Added the *charset* option. + + +-.. function:: getaddresses(fieldvalues) ++.. function:: getaddresses(fieldvalues, *, strict=True) + + This method returns a list of 2-tuples of the form returned by ``parseaddr()``. + *fieldvalues* is a sequence of header field values as might be returned by +- :meth:`Message.get_all `. Here's a simple +- example that gets all the recipients of a message:: ++ :meth:`Message.get_all `. ++ ++ If *strict* is true, use a strict parser which rejects malformed inputs. ++ ++ Here's a simple example that gets all the recipients of a message:: + + from email.utils import getaddresses + +@@ -99,6 +107,9 @@ of the new API. + resent_ccs = msg.get_all('resent-cc', []) + all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) + ++ .. versionchanged:: 3.13 ++ Add *strict* optional parameter and reject malformed inputs by default. ++ + + .. function:: parsedate(date) + +diff --git a/Lib/email/utils.py b/Lib/email/utils.py +index cfdfeb3f1a..9522341fab 100644 +--- a/Lib/email/utils.py ++++ b/Lib/email/utils.py +@@ -48,6 +48,7 @@ + specialsre = re.compile(r'[][\\()<>@,:;".]') + escapesre = re.compile(r'[\\"]') + ++ + def _has_surrogates(s): + """Return True if s contains surrogate-escaped binary data.""" + # This check is based on the fact that unless there are surrogates, utf8 +@@ -106,12 +107,127 @@ def formataddr(pair, charset='utf-8'): + return address + + ++def _iter_escaped_chars(addr): ++ pos = 0 ++ escape = False ++ for pos, ch in enumerate(addr): ++ if escape: ++ yield (pos, '\\' + ch) ++ escape = False ++ elif ch == '\\': ++ escape = True ++ else: ++ yield (pos, ch) ++ if escape: ++ yield (pos, '\\') + +-def getaddresses(fieldvalues): +- """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" +- all = COMMASPACE.join(str(v) for v in fieldvalues) +- a = _AddressList(all) +- return a.addresslist ++ ++def _strip_quoted_realnames(addr): ++ """Strip real names between quotes.""" ++ if '"' not in addr: ++ # Fast path ++ return addr ++ ++ start = 0 ++ open_pos = None ++ result = [] ++ for pos, ch in _iter_escaped_chars(addr): ++ if ch == '"': ++ if open_pos is None: ++ open_pos = pos ++ else: ++ if start != open_pos: ++ result.append(addr[start:open_pos]) ++ start = pos + 1 ++ open_pos = None ++ ++ if start < len(addr): ++ result.append(addr[start:]) ++ ++ return ''.join(result) ++ ++ ++supports_strict_parsing = True ++ ++def getaddresses(fieldvalues, *, strict=True): ++ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. ++ ++ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in ++ its place. ++ ++ If strict is true, use a strict parser which rejects malformed inputs. ++ """ ++ ++ # If strict is true, if the resulting list of parsed addresses is greater ++ # than the number of fieldvalues in the input list, a parsing error has ++ # occurred and consequently a list containing a single empty 2-tuple [('', ++ # '')] is returned in its place. This is done to avoid invalid output. ++ # ++ # Malformed input: getaddresses(['alice@example.com ']) ++ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')] ++ # Safe output: [('', '')] ++ ++ if not strict: ++ all = COMMASPACE.join(str(v) for v in fieldvalues) ++ a = _AddressList(all) ++ return a.addresslist ++ ++ fieldvalues = [str(v) for v in fieldvalues] ++ fieldvalues = _pre_parse_validation(fieldvalues) ++ addr = COMMASPACE.join(fieldvalues) ++ a = _AddressList(addr) ++ result = _post_parse_validation(a.addresslist) ++ ++ # Treat output as invalid if the number of addresses is not equal to the ++ # expected number of addresses. ++ n = 0 ++ for v in fieldvalues: ++ # When a comma is used in the Real Name part it is not a deliminator. ++ # So strip those out before counting the commas. ++ v = _strip_quoted_realnames(v) ++ # Expected number of addresses: 1 + number of commas ++ n += 1 + v.count(',') ++ if len(result) != n: ++ return [('', '')] ++ ++ return result ++ ++ ++def _check_parenthesis(addr): ++ # Ignore parenthesis in quoted real names. ++ addr = _strip_quoted_realnames(addr) ++ ++ opens = 0 ++ for pos, ch in _iter_escaped_chars(addr): ++ if ch == '(': ++ opens += 1 ++ elif ch == ')': ++ opens -= 1 ++ if opens < 0: ++ return False ++ return (opens == 0) ++ ++ ++def _pre_parse_validation(email_header_fields): ++ accepted_values = [] ++ for v in email_header_fields: ++ if not _check_parenthesis(v): ++ v = "('', '')" ++ accepted_values.append(v) ++ ++ return accepted_values ++ ++ ++def _post_parse_validation(parsed_email_header_tuples): ++ accepted_values = [] ++ # The parser would have parsed a correctly formatted domain-literal ++ # The existence of an [ after parsing indicates a parsing failure ++ for v in parsed_email_header_tuples: ++ if '[' in v[1]: ++ v = ('', '') ++ accepted_values.append(v) ++ ++ return accepted_values + + + def _format_timetuple_and_zone(timetuple, zone): +@@ -205,16 +321,33 @@ def parsedate_to_datetime(data): + tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) + + +-def parseaddr(addr): ++def parseaddr(addr, *, strict=True): + """ + Parse addr into its constituent realname and email address parts. + + Return a tuple of realname and email address, unless the parse fails, in + which case return a 2-tuple of ('', ''). ++ ++ If strict is True, use a strict parser which rejects malformed inputs. + """ +- addrs = _AddressList(addr).addresslist +- if not addrs: +- return '', '' ++ if not strict: ++ addrs = _AddressList(addr).addresslist ++ if not addrs: ++ return ('', '') ++ return addrs[0] ++ ++ if isinstance(addr, list): ++ addr = addr[0] ++ ++ if not isinstance(addr, str): ++ return ('', '') ++ ++ addr = _pre_parse_validation([addr])[0] ++ addrs = _post_parse_validation(_AddressList(addr).addresslist) ++ ++ if not addrs or len(addrs) > 1: ++ return ('', '') ++ + return addrs[0] + + +diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py +index 8b16cca9bf..5b19bb38f6 100644 +--- a/Lib/test/test_email/test_email.py ++++ b/Lib/test/test_email/test_email.py +@@ -16,6 +16,7 @@ + + import email + import email.policy ++import email.utils + + from email.charset import Charset + from email.generator import Generator, DecodedGenerator, BytesGenerator +@@ -3288,15 +3289,154 @@ def test_getaddresses(self): + [('Al Person', 'aperson@dom.ain'), + ('Bud Person', 'bperson@dom.ain')]) + ++ def test_getaddresses_comma_in_name(self): ++ """GH-106669 regression test.""" ++ self.assertEqual( ++ utils.getaddresses( ++ [ ++ '"Bud, Person" ', ++ 'aperson@dom.ain (Al Person)', ++ '"Mariusz Felisiak" ', ++ ] ++ ), ++ [ ++ ('Bud, Person', 'bperson@dom.ain'), ++ ('Al Person', 'aperson@dom.ain'), ++ ('Mariusz Felisiak', 'to@example.com'), ++ ], ++ ) ++ ++ def test_parsing_errors(self): ++ """Test for parsing errors from CVE-2023-27043 and CVE-2019-16056""" ++ alice = 'alice@example.org' ++ bob = 'bob@example.com' ++ empty = ('', '') ++ ++ # Test utils.getaddresses() and utils.parseaddr() on malformed email ++ # addresses: default behavior (strict=True) rejects malformed address, ++ # and strict=False which tolerates malformed address. ++ for invalid_separator, expected_non_strict in ( ++ ('(', [(f'<{bob}>', alice)]), ++ (')', [('', alice), empty, ('', bob)]), ++ ('<', [('', alice), empty, ('', bob), empty]), ++ ('>', [('', alice), empty, ('', bob)]), ++ ('[', [('', f'{alice}[<{bob}>]')]), ++ (']', [('', alice), empty, ('', bob)]), ++ ('@', [empty, empty, ('', bob)]), ++ (';', [('', alice), empty, ('', bob)]), ++ (':', [('', alice), ('', bob)]), ++ ('.', [('', alice + '.'), ('', bob)]), ++ ('"', [('', alice), ('', f'<{bob}>')]), ++ ): ++ address = f'{alice}{invalid_separator}<{bob}>' ++ with self.subTest(address=address): ++ self.assertEqual(utils.getaddresses([address]), ++ [empty]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ expected_non_strict) ++ ++ self.assertEqual(utils.parseaddr([address]), ++ empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Comma (',') is treated differently depending on strict parameter. ++ # Comma without quotes. ++ address = f'{alice},<{bob}>' ++ self.assertEqual(utils.getaddresses([address]), ++ [('', alice), ('', bob)]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('', alice), ('', bob)]) ++ self.assertEqual(utils.parseaddr([address]), ++ empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Real name between quotes containing comma. ++ address = '"Alice, alice@example.org" ' ++ expected_strict = ('Alice, alice@example.org', 'bob@example.com') ++ self.assertEqual(utils.getaddresses([address]), [expected_strict]) ++ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) ++ self.assertEqual(utils.parseaddr([address]), expected_strict) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Valid parenthesis in comments. ++ address = 'alice@example.org (Alice)' ++ expected_strict = ('Alice', 'alice@example.org') ++ self.assertEqual(utils.getaddresses([address]), [expected_strict]) ++ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) ++ self.assertEqual(utils.parseaddr([address]), expected_strict) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Invalid parenthesis in comments. ++ address = 'alice@example.org )Alice(' ++ self.assertEqual(utils.getaddresses([address]), [empty]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('', 'alice@example.org'), ('', ''), ('', 'Alice')]) ++ self.assertEqual(utils.parseaddr([address]), empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Two addresses with quotes separated by comma. ++ address = '"Jane Doe" , "John Doe" ' ++ self.assertEqual(utils.getaddresses([address]), ++ [('Jane Doe', 'jane@example.net'), ++ ('John Doe', 'john@example.net')]) ++ self.assertEqual(utils.getaddresses([address], strict=False), ++ [('Jane Doe', 'jane@example.net'), ++ ('John Doe', 'john@example.net')]) ++ self.assertEqual(utils.parseaddr([address]), empty) ++ self.assertEqual(utils.parseaddr([address], strict=False), ++ ('', address)) ++ ++ # Test email.utils.supports_strict_parsing attribute ++ self.assertEqual(email.utils.supports_strict_parsing, True) ++ + def test_getaddresses_nasty(self): +- eq = self.assertEqual +- eq(utils.getaddresses(['foo: ;']), [('', '')]) +- eq(utils.getaddresses( +- ['[]*-- =~$']), +- [('', ''), ('', ''), ('', '*--')]) +- eq(utils.getaddresses( +- ['foo: ;', '"Jason R. Mastaler" ']), +- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) ++ for addresses, expected in ( ++ (['"Sürname, Firstname" '], ++ [('Sürname, Firstname', 'to@example.com')]), ++ ++ (['foo: ;'], ++ [('', '')]), ++ ++ (['foo: ;', '"Jason R. Mastaler" '], ++ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]), ++ ++ ([r'Pete(A nice \) chap) '], ++ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]), ++ ++ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'], ++ [('', '')]), ++ ++ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'], ++ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]), ++ ++ (['John Doe '], ++ [('John Doe (comment)', 'jdoe@machine.example')]), ++ ++ (['"Mary Smith: Personal Account" '], ++ [('Mary Smith: Personal Account', 'smith@home.example')]), ++ ++ (['Undisclosed recipients:;'], ++ [('', '')]), ++ ++ ([r', "Giant; \"Big\" Box" '], ++ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]), ++ ): ++ with self.subTest(addresses=addresses): ++ self.assertEqual(utils.getaddresses(addresses), ++ expected) ++ self.assertEqual(utils.getaddresses(addresses, strict=False), ++ expected) ++ ++ addresses = ['[]*-- =~$'] ++ self.assertEqual(utils.getaddresses(addresses), ++ [('', '')]) ++ self.assertEqual(utils.getaddresses(addresses, strict=False), ++ [('', ''), ('', ''), ('', '*--')]) + + def test_getaddresses_embedded_comment(self): + """Test proper handling of a nested comment""" +@@ -3485,6 +3625,54 @@ def test_mime_classes_policy_argument(self): + m = cls(*constructor, policy=email.policy.default) + self.assertIs(m.policy, email.policy.default) + ++ def test_iter_escaped_chars(self): ++ self.assertEqual(list(utils._iter_escaped_chars(r'a\\b\"c\\"d')), ++ [(0, 'a'), ++ (2, '\\\\'), ++ (3, 'b'), ++ (5, '\\"'), ++ (6, 'c'), ++ (8, '\\\\'), ++ (9, '"'), ++ (10, 'd')]) ++ self.assertEqual(list(utils._iter_escaped_chars('a\\')), ++ [(0, 'a'), (1, '\\')]) ++ ++ def test_strip_quoted_realnames(self): ++ def check(addr, expected): ++ self.assertEqual(utils._strip_quoted_realnames(addr), expected) ++ ++ check('"Jane Doe" , "John Doe" ', ++ ' , ') ++ check(r'"Jane \"Doe\"." ', ++ ' ') ++ ++ # special cases ++ check(r'before"name"after', 'beforeafter') ++ check(r'before"name"', 'before') ++ check(r'b"name"', 'b') # single char ++ check(r'"name"after', 'after') ++ check(r'"name"a', 'a') # single char ++ check(r'"name"', '') ++ ++ # no change ++ for addr in ( ++ 'Jane Doe , John Doe ', ++ 'lone " quote', ++ ): ++ self.assertEqual(utils._strip_quoted_realnames(addr), addr) ++ ++ ++ def test_check_parenthesis(self): ++ addr = 'alice@example.net' ++ self.assertTrue(utils._check_parenthesis(f'{addr} (Alice)')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} )Alice(')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} (Alice))')) ++ self.assertFalse(utils._check_parenthesis(f'{addr} ((Alice)')) ++ ++ # Ignore real name between quotes ++ self.assertTrue(utils._check_parenthesis(f'")Alice((" {addr}')) ++ + + # Test the iterator/generators + class TestIterators(TestEmailBase): +diff --git a/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst +new file mode 100644 +index 0000000000..3d0e9e4078 +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst +@@ -0,0 +1,8 @@ ++:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now ++return ``('', '')`` 2-tuples in more situations where invalid email ++addresses are encountered instead of potentially inaccurate values. Add ++optional *strict* parameter to these two functions: use ``strict=False`` to ++get the old behavior, accept malformed inputs. ++``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check ++if the *strict* paramater is available. Patch by Thomas Dwyer and Victor ++Stinner to improve the CVE-2023-27043 fix. diff --git a/python3.10.spec b/python3.10.spec index 11b235e..2d24e03 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -314,6 +314,15 @@ Patch251: 00251-change-user-install-location.patch # https://github.com/GrahamDumpleton/mod_wsgi/issues/730 Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch +# 00415 # 9ca4533e0b4a03d919953017026f66c6a060756e +# [CVE-2023-27043] gh-102988: Reject malformed addresses in email.parseaddr() (#111116) +# +# Detect email address parsing errors and return empty tuple to +# indicate the parsing error (old API). Add an optional 'strict' +# parameter to getaddresses() and parseaddr() functions. Patch by +# Thomas Dwyer. +Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1587,6 +1596,9 @@ CheckPython optimized # ====================================================== %changelog +* Mon Dec 18 2023 Lumír Balhar - 3.10.13-2 +- Security fix for CVE-2023-27043 (rhbz#2196187) + * Mon Aug 28 2023 Tomáš Hrnčiar - 3.10.13-1 - Update to 3.10.13 From 8e56c79b2a43ba1872d8817df29f3b6dfbbf12e9 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Mon, 22 Jan 2024 09:21:36 +0000 Subject: [PATCH 12/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 2d24e03..0f20345 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -1596,6 +1596,9 @@ CheckPython optimized # ====================================================== %changelog +* Mon Jan 22 2024 Fedora Release Engineering - 3.10.13-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Mon Dec 18 2023 Lumír Balhar - 3.10.13-2 - Security fix for CVE-2023-27043 (rhbz#2196187) From 740efdfb461ab36d15cea8f4acdc4640967cc199 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 26 Jan 2024 13:31:26 +0000 Subject: [PATCH 13/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 0f20345..39aea88 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 4%{?dist} License: Python-2.0.1 @@ -1596,6 +1596,9 @@ CheckPython optimized # ====================================================== %changelog +* Fri Jan 26 2024 Fedora Release Engineering - 3.10.13-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Mon Jan 22 2024 Fedora Release Engineering - 3.10.13-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From 433e38e58c506b7ce0fb8d84614731afd7912b86 Mon Sep 17 00:00:00 2001 From: Karolina Surma Date: Thu, 25 Jan 2024 13:52:35 +0100 Subject: [PATCH 14/53] Fix test_zlib with zlib-ng-compat --- ...h-non-int-suffix-gh-112771-gh-112774.patch | 66 +++++++++++++++++++ python3.10.spec | 11 +++- 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch diff --git a/00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch b/00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch new file mode 100644 index 0000000..1e718a6 --- /dev/null +++ b/00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch @@ -0,0 +1,66 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= +Date: Tue, 5 Dec 2023 21:02:06 +0100 +Subject: [PATCH] 00419: gh-112769: test_zlib: Fix comparison of + ZLIB_RUNTIME_VERSION with non-int suffix (GH-112771) (GH-112774) + +zlib-ng defines the version as "1.3.0.zlib-ng". +(cherry picked from commit d384813ff18b33280a90b6d2011654528a2b6ad1) +--- + Lib/test/test_zlib.py | 28 ++++++++++++++++------------ + 1 file changed, 16 insertions(+), 12 deletions(-) + +diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py +index cb0610837b..98cac4fb91 100644 +--- a/Lib/test/test_zlib.py ++++ b/Lib/test/test_zlib.py +@@ -19,6 +19,20 @@ + 'requires Decompress.copy()') + + ++def _zlib_runtime_version_tuple(zlib_version=zlib.ZLIB_RUNTIME_VERSION): ++ # Register "1.2.3" as "1.2.3.0" ++ # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux" ++ v = zlib_version.split('-', 1)[0].split('.') ++ if len(v) < 4: ++ v.append('0') ++ elif not v[-1].isnumeric(): ++ v[-1] = '0' ++ return tuple(map(int, v)) ++ ++ ++ZLIB_RUNTIME_VERSION_TUPLE = _zlib_runtime_version_tuple() ++ ++ + class VersionTestCase(unittest.TestCase): + + def test_library_version(self): +@@ -445,9 +459,8 @@ def test_flushes(self): + sync_opt = ['Z_NO_FLUSH', 'Z_SYNC_FLUSH', 'Z_FULL_FLUSH', + 'Z_PARTIAL_FLUSH'] + +- ver = tuple(int(v) for v in zlib.ZLIB_RUNTIME_VERSION.split('.')) + # Z_BLOCK has a known failure prior to 1.2.5.3 +- if ver >= (1, 2, 5, 3): ++ if ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 5, 3): + sync_opt.append('Z_BLOCK') + + sync_opt = [getattr(zlib, opt) for opt in sync_opt +@@ -776,16 +789,7 @@ def test_large_unconsumed_tail(self, size): + + def test_wbits(self): + # wbits=0 only supported since zlib v1.2.3.5 +- # Register "1.2.3" as "1.2.3.0" +- # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux" +- v = zlib.ZLIB_RUNTIME_VERSION.split('-', 1)[0].split('.') +- if len(v) < 4: +- v.append('0') +- elif not v[-1].isnumeric(): +- v[-1] = '0' +- +- v = tuple(map(int, v)) +- supports_wbits_0 = v >= (1, 2, 3, 5) ++ supports_wbits_0 = ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 3, 5) + + co = zlib.compressobj(level=1, wbits=15) + zlib15 = co.compress(HAMLET_SCENE) + co.flush() diff --git a/python3.10.spec b/python3.10.spec index 39aea88..0a1b1d6 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 4%{?dist} +Release: 5%{?dist} License: Python-2.0.1 @@ -323,6 +323,12 @@ Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-g # Thomas Dwyer. Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch +# 00419 # f13682530cc7e4daec2e40acd56508846fdd3aad +# gh-112769: test_zlib: Fix comparison of ZLIB_RUNTIME_VERSION with non-int suffix (GH-112771) (GH-112774) +# +# zlib-ng defines the version as "1.3.0.zlib-ng". +Patch419: 00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1596,6 +1602,9 @@ CheckPython optimized # ====================================================== %changelog +* Mon Jan 29 2024 Karolina Surma - 3.10.13-5 +- Fix test_zlib when building with zlib-ng-compat + * Fri Jan 26 2024 Fedora Release Engineering - 3.10.13-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From f8879650f9b17a9568d6e2d23443bd569c2e78e5 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Wed, 28 Feb 2024 17:08:06 +0100 Subject: [PATCH 15/53] Fix tests for XMLPullParser with Expat 2.6.0 See also: https://bugzilla.redhat.com/2264859 --- ...s-for-xmlpullparser-with-expat-2-6-0.patch | 107 ++++++++++++++++++ python3.10.spec | 12 +- 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch diff --git a/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch b/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch new file mode 100644 index 0000000..a54d321 --- /dev/null +++ b/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Serhiy Storchaka +Date: Sun, 11 Feb 2024 12:08:39 +0200 +Subject: [PATCH] 00422: gh-115133: Fix tests for XMLPullParser with Expat + 2.6.0 + +Feeding the parser by too small chunks defers parsing to prevent +CVE-2023-52425. Future versions of Expat may be more reactive. + +(cherry picked from commit 4a08e7b3431cd32a0daf22a33421cd3035343dc4) +--- + Lib/test/test_xml_etree.py | 58 ++++++++++++------- + ...-02-08-14-21-28.gh-issue-115133.ycl4ko.rst | 2 + + 2 files changed, 38 insertions(+), 22 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst + +diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py +index 940e02630e..c03d1bacda 100644 +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -13,6 +13,7 @@ + import operator + import os + import pickle ++import pyexpat + import sys + import textwrap + import types +@@ -120,6 +121,10 @@ + + """ + ++fails_with_expat_2_6_0 = (unittest.expectedFailure ++ if pyexpat.version_info >= (2, 6, 0) else ++ lambda test: test) ++ + def checkwarnings(*filters, quiet=False): + def decorator(test): + def newtest(*args, **kwargs): +@@ -1396,28 +1401,37 @@ def assert_event_tags(self, parser, expected, max_events=None): + self.assertEqual([(action, elem.tag) for action, elem in events], + expected) + +- def test_simple_xml(self): +- for chunk_size in (None, 1, 5): +- with self.subTest(chunk_size=chunk_size): +- parser = ET.XMLPullParser() +- self.assert_event_tags(parser, []) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, []) +- self._feed(parser, +- "\n text\n", chunk_size) +- self.assert_event_tags(parser, [('end', 'element')]) +- self._feed(parser, "texttail\n", chunk_size) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, [ +- ('end', 'element'), +- ('end', 'empty-element'), +- ]) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, [('end', 'root')]) +- self.assertIsNone(parser.close()) ++ def test_simple_xml(self, chunk_size=None): ++ parser = ET.XMLPullParser() ++ self.assert_event_tags(parser, []) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, []) ++ self._feed(parser, ++ "\n text\n", chunk_size) ++ self.assert_event_tags(parser, [('end', 'element')]) ++ self._feed(parser, "texttail\n", chunk_size) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, [ ++ ('end', 'element'), ++ ('end', 'empty-element'), ++ ]) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, [('end', 'root')]) ++ self.assertIsNone(parser.close()) ++ ++ @fails_with_expat_2_6_0 ++ def test_simple_xml_chunk_1(self): ++ self.test_simple_xml(chunk_size=1) ++ ++ @fails_with_expat_2_6_0 ++ def test_simple_xml_chunk_5(self): ++ self.test_simple_xml(chunk_size=5) ++ ++ def test_simple_xml_chunk_22(self): ++ self.test_simple_xml(chunk_size=22) + + def test_feed_while_iterating(self): + parser = ET.XMLPullParser() +diff --git a/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst b/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst +new file mode 100644 +index 0000000000..6f1015235c +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst +@@ -0,0 +1,2 @@ ++Fix tests for :class:`~xml.etree.ElementTree.XMLPullParser` with Expat ++2.6.0. diff --git a/python3.10.spec b/python3.10.spec index 0a1b1d6..e4e3677 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 5%{?dist} +Release: 6%{?dist} License: Python-2.0.1 @@ -329,6 +329,13 @@ Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-par # zlib-ng defines the version as "1.3.0.zlib-ng". Patch419: 00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch +# 00422 # a353cebef737c41420dc7ae2469dd657371b8881 +# gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 +# +# Feeding the parser by too small chunks defers parsing to prevent +# CVE-2023-52425. Future versions of Expat may be more reactive. +Patch422: 00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1602,6 +1609,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Feb 28 2024 Charalampos Stratakis - 3.10.13-6 +- Fix tests for XMLPullParser with Expat 2.6.0 + * Mon Jan 29 2024 Karolina Surma - 3.10.13-5 - Fix test_zlib when building with zlib-ng-compat From d897af59dab6d8a80b26d3ed5a4ec882ee285565 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Wed, 28 Feb 2024 17:08:06 +0100 Subject: [PATCH 16/53] Fix tests for XMLPullParser with Expat 2.6.0 See also: https://bugzilla.redhat.com/2264859 --- ...s-for-xmlpullparser-with-expat-2-6-0.patch | 107 ++++++++++++++++++ python3.10.spec | 12 +- 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch diff --git a/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch b/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch new file mode 100644 index 0000000..a54d321 --- /dev/null +++ b/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Serhiy Storchaka +Date: Sun, 11 Feb 2024 12:08:39 +0200 +Subject: [PATCH] 00422: gh-115133: Fix tests for XMLPullParser with Expat + 2.6.0 + +Feeding the parser by too small chunks defers parsing to prevent +CVE-2023-52425. Future versions of Expat may be more reactive. + +(cherry picked from commit 4a08e7b3431cd32a0daf22a33421cd3035343dc4) +--- + Lib/test/test_xml_etree.py | 58 ++++++++++++------- + ...-02-08-14-21-28.gh-issue-115133.ycl4ko.rst | 2 + + 2 files changed, 38 insertions(+), 22 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst + +diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py +index 940e02630e..c03d1bacda 100644 +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -13,6 +13,7 @@ + import operator + import os + import pickle ++import pyexpat + import sys + import textwrap + import types +@@ -120,6 +121,10 @@ + + """ + ++fails_with_expat_2_6_0 = (unittest.expectedFailure ++ if pyexpat.version_info >= (2, 6, 0) else ++ lambda test: test) ++ + def checkwarnings(*filters, quiet=False): + def decorator(test): + def newtest(*args, **kwargs): +@@ -1396,28 +1401,37 @@ def assert_event_tags(self, parser, expected, max_events=None): + self.assertEqual([(action, elem.tag) for action, elem in events], + expected) + +- def test_simple_xml(self): +- for chunk_size in (None, 1, 5): +- with self.subTest(chunk_size=chunk_size): +- parser = ET.XMLPullParser() +- self.assert_event_tags(parser, []) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, []) +- self._feed(parser, +- "\n text\n", chunk_size) +- self.assert_event_tags(parser, [('end', 'element')]) +- self._feed(parser, "texttail\n", chunk_size) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, [ +- ('end', 'element'), +- ('end', 'empty-element'), +- ]) +- self._feed(parser, "\n", chunk_size) +- self.assert_event_tags(parser, [('end', 'root')]) +- self.assertIsNone(parser.close()) ++ def test_simple_xml(self, chunk_size=None): ++ parser = ET.XMLPullParser() ++ self.assert_event_tags(parser, []) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, []) ++ self._feed(parser, ++ "\n text\n", chunk_size) ++ self.assert_event_tags(parser, [('end', 'element')]) ++ self._feed(parser, "texttail\n", chunk_size) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, [ ++ ('end', 'element'), ++ ('end', 'empty-element'), ++ ]) ++ self._feed(parser, "\n", chunk_size) ++ self.assert_event_tags(parser, [('end', 'root')]) ++ self.assertIsNone(parser.close()) ++ ++ @fails_with_expat_2_6_0 ++ def test_simple_xml_chunk_1(self): ++ self.test_simple_xml(chunk_size=1) ++ ++ @fails_with_expat_2_6_0 ++ def test_simple_xml_chunk_5(self): ++ self.test_simple_xml(chunk_size=5) ++ ++ def test_simple_xml_chunk_22(self): ++ self.test_simple_xml(chunk_size=22) + + def test_feed_while_iterating(self): + parser = ET.XMLPullParser() +diff --git a/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst b/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst +new file mode 100644 +index 0000000000..6f1015235c +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst +@@ -0,0 +1,2 @@ ++Fix tests for :class:`~xml.etree.ElementTree.XMLPullParser` with Expat ++2.6.0. diff --git a/python3.10.spec b/python3.10.spec index 2d24e03..8164713 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -323,6 +323,13 @@ Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-g # Thomas Dwyer. Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch +# 00422 # a353cebef737c41420dc7ae2469dd657371b8881 +# gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 +# +# Feeding the parser by too small chunks defers parsing to prevent +# CVE-2023-52425. Future versions of Expat may be more reactive. +Patch422: 00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1596,6 +1603,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Feb 28 2024 Charalampos Stratakis - 3.10.13-3 +- Fix tests for XMLPullParser with Expat 2.6.0 + * Mon Dec 18 2023 Lumír Balhar - 3.10.13-2 - Security fix for CVE-2023-27043 (rhbz#2196187) From 8ba4297490170e14809514c91252446da29f9690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Wed, 20 Mar 2024 11:19:24 +0100 Subject: [PATCH 17/53] Update to 3.10.14 --- 00251-change-user-install-location.patch | 4 +- ...s-for-xmlpullparser-with-expat-2-6-0.patch | 107 -- pgp_keys.asc | 109 -- pgp_keys_combined.asc | 1281 +++++++++++++++++ python3.10.spec | 23 +- sources | 4 +- 6 files changed, 1297 insertions(+), 231 deletions(-) delete mode 100644 00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch delete mode 100644 pgp_keys.asc create mode 100644 pgp_keys_combined.asc diff --git a/00251-change-user-install-location.patch b/00251-change-user-install-location.patch index dc9c257..b0a4c9c 100644 --- a/00251-change-user-install-location.patch +++ b/00251-change-user-install-location.patch @@ -61,10 +61,10 @@ index 01d5331a63..79f70f0de4 100644 else: if self.exec_prefix is None: diff --git a/Lib/site.py b/Lib/site.py -index 939893eb5e..d1316c3355 100644 +index 5302037e0b..4ef002c6bc 100644 --- a/Lib/site.py +++ b/Lib/site.py -@@ -380,8 +380,15 @@ def getsitepackages(prefixes=None): +@@ -390,8 +390,15 @@ def getsitepackages(prefixes=None): return sitepackages def addsitepackages(known_paths, prefixes=None): diff --git a/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch b/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch deleted file mode 100644 index a54d321..0000000 --- a/00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Serhiy Storchaka -Date: Sun, 11 Feb 2024 12:08:39 +0200 -Subject: [PATCH] 00422: gh-115133: Fix tests for XMLPullParser with Expat - 2.6.0 - -Feeding the parser by too small chunks defers parsing to prevent -CVE-2023-52425. Future versions of Expat may be more reactive. - -(cherry picked from commit 4a08e7b3431cd32a0daf22a33421cd3035343dc4) ---- - Lib/test/test_xml_etree.py | 58 ++++++++++++------- - ...-02-08-14-21-28.gh-issue-115133.ycl4ko.rst | 2 + - 2 files changed, 38 insertions(+), 22 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst - -diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py -index 940e02630e..c03d1bacda 100644 ---- a/Lib/test/test_xml_etree.py -+++ b/Lib/test/test_xml_etree.py -@@ -13,6 +13,7 @@ - import operator - import os - import pickle -+import pyexpat - import sys - import textwrap - import types -@@ -120,6 +121,10 @@ - - """ - -+fails_with_expat_2_6_0 = (unittest.expectedFailure -+ if pyexpat.version_info >= (2, 6, 0) else -+ lambda test: test) -+ - def checkwarnings(*filters, quiet=False): - def decorator(test): - def newtest(*args, **kwargs): -@@ -1396,28 +1401,37 @@ def assert_event_tags(self, parser, expected, max_events=None): - self.assertEqual([(action, elem.tag) for action, elem in events], - expected) - -- def test_simple_xml(self): -- for chunk_size in (None, 1, 5): -- with self.subTest(chunk_size=chunk_size): -- parser = ET.XMLPullParser() -- self.assert_event_tags(parser, []) -- self._feed(parser, "\n", chunk_size) -- self.assert_event_tags(parser, []) -- self._feed(parser, -- "\n text\n", chunk_size) -- self.assert_event_tags(parser, [('end', 'element')]) -- self._feed(parser, "texttail\n", chunk_size) -- self._feed(parser, "\n", chunk_size) -- self.assert_event_tags(parser, [ -- ('end', 'element'), -- ('end', 'empty-element'), -- ]) -- self._feed(parser, "\n", chunk_size) -- self.assert_event_tags(parser, [('end', 'root')]) -- self.assertIsNone(parser.close()) -+ def test_simple_xml(self, chunk_size=None): -+ parser = ET.XMLPullParser() -+ self.assert_event_tags(parser, []) -+ self._feed(parser, "\n", chunk_size) -+ self.assert_event_tags(parser, []) -+ self._feed(parser, -+ "\n text\n", chunk_size) -+ self.assert_event_tags(parser, [('end', 'element')]) -+ self._feed(parser, "texttail\n", chunk_size) -+ self._feed(parser, "\n", chunk_size) -+ self.assert_event_tags(parser, [ -+ ('end', 'element'), -+ ('end', 'empty-element'), -+ ]) -+ self._feed(parser, "\n", chunk_size) -+ self.assert_event_tags(parser, [('end', 'root')]) -+ self.assertIsNone(parser.close()) -+ -+ @fails_with_expat_2_6_0 -+ def test_simple_xml_chunk_1(self): -+ self.test_simple_xml(chunk_size=1) -+ -+ @fails_with_expat_2_6_0 -+ def test_simple_xml_chunk_5(self): -+ self.test_simple_xml(chunk_size=5) -+ -+ def test_simple_xml_chunk_22(self): -+ self.test_simple_xml(chunk_size=22) - - def test_feed_while_iterating(self): - parser = ET.XMLPullParser() -diff --git a/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst b/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst -new file mode 100644 -index 0000000000..6f1015235c ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2024-02-08-14-21-28.gh-issue-115133.ycl4ko.rst -@@ -0,0 +1,2 @@ -+Fix tests for :class:`~xml.etree.ElementTree.XMLPullParser` with Expat -+2.6.0. diff --git a/pgp_keys.asc b/pgp_keys.asc deleted file mode 100644 index 11dccb8..0000000 --- a/pgp_keys.asc +++ /dev/null @@ -1,109 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFq+ToQBEADRYvIVtbK6owynD3j3nxwpW2KEk/p+aDvtXmc2SR2dBcZ8sFW2 -R5vEsG8d3/D3wgv5pcL3KfNNXQYUnXVbobrFUUWQYc79qIsE3MgiPf5NVOtwKPUR -i5g9YJgKvpBxkQfqp3LYGm9ZBtwo3DVLA3yn7KsazCmAgTNFJYw7ku1XxgmIzY6K -5J30DfbJiqDqj4f9GslCCCCH3qiPnuLG/HUyVLHMpbWlaiy9NI0GcaLxjJewHj9w -W2D2lydkxe5JGo7egUkV3ILcuLVSVKA35SKY27dYqfuyqp9tAzaRbjDYjsYdHA6G -BqrNrKBn/GwlFDPrVdcvN3ZSY2wMLTxWE3Axc/FweuHxFnou/80FwX7F3JD+oEQ6 -rofmcxOBCC7J98I7HZAhP9jBn88XIS2hztbLq8d6rZJZRtcz0k61VR0ddO+TrFmf -9rMYCPgCckRtVxeFIVIabrN1IzKynLFeo040h8hSGswd6YKDOVwjJY6Oa6EmVefZ -a8QSt4+M65RSzH6SEPY008F3nJUAK6MEkzTak+tFltZNrVWu8p2xd1j9nmxAwEhZ -/lgbxLqzYgaUWmfyHeZ8yVA0MhHzdiAL8nVUEdG3KecIq0RWCJLGLWWIjd6KAJl1 -yAmhRYKK/sjPDsL3elHsFACfZbyx3o5GGQNlas1FYoPLWbaNGaJtgFTF2QARAQAB -tCtQYWJsbyBHYWxpbmRvIFNhbGdhZG8gPHBhYmxvZ3NhbEBnbWFpbC5jb20+iQJO -BBMBCgA4FiEEoDXIwZIZuoIezqhrZOYo+NaEaW0FAlq+ToQCGwMFCwkIBwMFFQoJ -CAsFFgIDAQACHgECF4AACgkQZOYo+NaEaW2bmA/+PXIap2udLoUVOHxnsIBdqYwp -sv1Aj5lfIJmNhmxPbHShwp1Jg+w4urxe+2Dj5ofKVlIo1i83bQkvnKJMDXDVuc/K -P6zqhBJ3rT4Q3qx2mzX8bIfQoJ2JHuH4lkP+I7doDcHHRyeNASyk72VdQmU4twNw -Ibn8nSNV6ThKHdoPYzVnO2rZUFcGIqH5HNsvR+B7cc1MBCHsgURYwSVhSePIFGlZ -iasdBD6QQkDSe4QWi7AcJFWFElw4kbOKJWxAWsrEk+tMXJVGRjnmL289EmPCx/vx -BqKy7Mse0yWCSRR3vB+O6TB1S5SgEyEgqlYsfGNv1qf/rfRD4KkyCbNU3LhY1Aim -vJP4pDW+KFxTk2Ks8vrx8gOSd2aFqPeO/pFDrpsF7PD62XwsfoXu4xc5V0Giw7r1 -Nai0nax7kOrldNF8TbbtRjW0jmoC7wLIDujAkwDIOroZ0CXA3N4HVHdSbrHm/urX -nyxJXupXAQNwGx64JCBcbF2fp3Kvu1VAXBEFnd01KaopthHcbG5pA50Kl2Vhe+98 -OdezUX42fHkQpQkB7HgtXfm6W1bw6YRBamrNvs1OoHBYmUjlECpe566IIu25Hc8s -x3qA+6eca7iqizyLG+WyMT8ZIYTWGAS59jxwR4esqGczbbZPSAPHFwLbGv7Wr0Rd -TPu5B0FcKpDkTd4IxQW5Ag0EWr5O2gEQAMjLe4CtbSfofmJrz5wfNkMVsZ81Gbqe -MoYd3dtkJnQYERUj8flzBj3ucaxGJ+Cuf7ybh3naPopKvEI1q0vkcgCDqrEgXK// -jKJbP28uPSMGhOG28q4PbamG55gy5FtM3ezzAxPWWKe9qBpV65GMmFy7eBQx2iJs -yiDIOOQQ4kraS+cTqNFimEXAGLCOQRNLcwIZzwAAHoW7HEpNUfVwaBD9kMlbo1ND -I60IKcNrNcmcmRxhJqfxjj8YBMwcKHO6GBE3AVpaE/+UO9zyr4TH+0YuQUgxKlPW -Dkg5XlkDo0S1GyLY5e9ckIDIlkTdDa2pOkoE2yB5MQCEga3YiHrKUVTTWaxn9XVJ -6x5ZjUF6bgSWGkrG5dUqSYoO1iDMuNVjtiujNyf/rvfj5cNxS7/lgxchhQKZHZXL -WVqxlneeVJ6s0P4+ROVG9ga2Sve7aUJ6wXIewZwulBcV2sE/W/DgxHgLBi53CUQt -vEzFzKvo48GnDqL5VYjA7l0HMYHd4GksCLi8E8U6Cgj+imXiM8voL7pHRZfs8mY8 -udR+UT4e1Scl2MYP2qBJ9/17B/X52B3s1EZdqI/r+hfOyqrhPs+dbAN0mtMPn68+ -nrvY1+nscvrSYEP6ZBlc9Hp2mgJdb6IcTvINXBEeLRjgc3pjViva443pkiFp9Axm -ecOckMKP3uSlABEBAAGJBGwEGAEKACAWIQSgNcjBkhm6gh7OqGtk5ij41oRpbQUC -Wr5O2gIbAgJACRBk5ij41oRpbcF0IAQZAQoAHRYhBM/cokWxBDzypfl4Zf/odAQW -i9hHBQJavk7aAAoJEP/odAQWi9hHr7YP/RCLre1CmOoWYpAtoa1yVCeYMDV6eQgL -B488/BEZHQE1zbrYy16XkhORob3JF/kUMjmJW7XaFF8FrWvRcdj/xaUGbOOEulKg -v+8zWfswYQRiZ4/JlwER4vRLi6fTE89MVER6Fkj2ASD4D2cifY+EztD4flV3sq3s -vIogGFaN9IvdrdeptOVGXs1RmAyoTsiS2mKQ6xsGh8B9ZAm55W8fBOGiSzLX21Xk -Ofdw53BrFQxn3cu/JgIKpdeZxgukcvEAI62B6X+YL6Na4j0eqEGLzsNtU1+xeJlo -WtVvmRwnRHGSxF6fzIZ3mk/p/aFiXAEq/xITCTY6tDv7x7pFE/RpdlJZyNJ+R5Y4 -SQiuDsylxNCa/4G5EB6q+7iVYtbEQ9MnZg2phowEE42tlj0rz8/rvDK3LH3xibot -KHIodCWKlWByxH99u2PuHUQ0c1oCVBUE1KkruMpvI236DpU/dvdq4JLSg/fWrys/ -VIjqLZgsIE5g/KO9XqngWHkLcBLh4CNAmHJ8Iia+s+/rfgsejQWB5uJb6eYg2JjB -4WP1EI0rULM6fdrCNB+MJ36wE2Lnb4bfT0phOMgjjH5/Ki7ZCbkxkOsBs4SRjiS+ -weCsmpAtMqodWY/Cnw9pWSA/qLSRD5/mKeb9SO6OZ/OPfAatwnGHsvZ2sAueC6rR -04W5BfXZWrnJUXQP/id/EKE1Ksp5fKoxSCbkKTCig+Sf5Afwe36yFN+niZBqzn5b -BgL/HIKaZM97oDHersPPANeEgS+JVlBf95iKIYnQbZP43FLVbvOuaINhBIVtFO54 -2Y7EYwl41kP7ILDElVy36KAmdQyBAfrjnZiRA70xShOxApLug1L0lxhR3YfmLwNi -RJ0V6KnYDKf0pfdhO9VFyFFWUojX1usn2SmSsXNizsNtvRqHXzPnX0rbJzZ9+N4O -9k1nxygYFG/2R/jGonVmTjRzcAHrAkNJETMWXMA7/8wRMDwluz8j+cCldey9x8Vk -JwgLGnZSbQtVpcFAnm5r/36Gt+9wc1VWMyrUrVr6Z679aqAbG7PMaeR5h5ygMj1k -VqRTYAUPSk1f8bZKRssQkQwEbp9dVIjm9SsR8VT7/tB+UuB85dABxgHfv3psJRT+ -tL8g9V7kSZqQfcLNGmvEVvr2Zl9NtxwXtsFM2OBprxCenwb+e9Ppm1LjfJG/NE72 -mAnOERfDaiLt4bqNo36Ei5sGCJ4Fx61phzNBXzkdRNM47i8J5UZRKFkE91c99BVM -HKUaY61NRK24fR0zP98ftDU82YFw0VRFJpTeBrO5ivN1MlQxUPzUWxKxMxO+20wa -UOXroEw11Tb4SRLGOla1pCl6lCUPJRy9IzadPDgTr/OTMkob/snt/XLdnV5/uQIN -BFq+TvoBEAC8Oy1g6pPWBbrCMhIq7VWY2fjylJ1fwg5BPXkOKVK1dsGYO4QD7oW9 -L0aSqcFSNFGF9Cl0Ri4TFXZC3hnG4HeSXUWApuKdBLn21H3jba36Ay1oGcGfdm0v -Zght4c6BlMVBpGCw2wIkJbUNEy6InMM+O8CCbbaH3iJkJ4141P7pODHignx5AmZI -conMui4YOhC+IXQXynVEv1Juk7erB1Nh1RcRvsA4lb44HWx49lIwe85ejOmoZ0O3 -6f9NJRer6bV0+rHWmg4IV5Q9h/Gn4IhEDZxA0DZl1RQI7dMgaMbIFbXGq7Kgzstz -EUnOoy29hXodxVmwIsMrAiQUYtwJ9hW+ESsw47+W2iPHVgviGWl7r/SgcgMYmf6m -5kiTBtwU7BQPS9G3zwwP2Rm3AA/6g39Q+tQKjOwi1I8+GZsY2On44Zly7BreBNg5 -4gJgdAGcMOYU9etr050clH3UpTYcAEtX++ahtOKhJgLIPNcIAQNlnifqvU0VYpgw -R4YpZ7hgg+AVDzC73PIM0lFI0XiDuqChbxE+K1jmLXWe5iJF0dzgVTwP+PmsifNZ -Wg3+YxSsS+hDMPQ2xPiQN49gT4JJDHcDuyhHyCGYgyMiVJCsku9KrkubbfVRivyN -ZF2Zfo3f+nbrRxsftz0yjAq8byCvb0V0XOpt4pJ/ddlug9ytRxALNwARAQABiQI2 -BBgBCgAgFiEEoDXIwZIZuoIezqhrZOYo+NaEaW0FAlq+TvoCGwwACgkQZOYo+NaE -aW3urA//UQ/cKQ7HvWjcLphzQOZc+6m5YL0wxvZkSjemU7mqjZdpacteIvRAoers -EqXHc208liIBtNfRzoreXdcXNzie65xXkrRnWoHVH/fTWy4lOnHr2CMXLeHjUgg/ -M6PYi8+sARm05YFB8nsYhlhx3IdLhcfeVVbJedQKO0yL3CK1okT30DUVq5Lq6X/K -DC6AxuJR3D6UMSoT0WLaoX8qbhAp88qLynInfBVL18d97h916WPLTPeP0eHwhwND -bYtKDCMDuKQ9XX5+QsNH0RmbxlX274LHrUMMvkLKxcfCBvP+iuqrBeIuoeVzXYJZ -j7ZJtEH79bW44eecl/CY/STFYgSQ2XGTp2BI2q60wAmtKlNhwxY5ena0FgyFl6Tm -5OBHW/Pwo+ndQJGfbrCyWkTgRay9c8er3gl3GQYIBH6X0kCiG7h/Epj0b5CHOPU5 -hCw0kEB8MB4poTIjeiY+Q01472/lQ68CL3DX158hR5d3XaPSIxAN+qFsfB1o316p -yjxhfK1MD/IfrOgjlggPPnc/KmLkCzpgdwKcZwLCdZq9hYBvF1Zs34HbaVMYbWTK -uxLowtXGU43vatCXXqmPOvl4/g4tZD6rysJDgOrHQnEHzT+Napn07s0BRC0IbbNn -FynUrkr5KMSuRz7Hg7xMApENOrb0nqdHSUJ914ZpuMIS6RhJgGu5Ag0EWr5PIAEQ -ALfh9vPD2B+miHDTMADI8aRZ7g9tnzynZYkk3+2sCiiusetsQQ+HIPJ/ASEJB7On -ane9dyT/LTRhrK9qaxgVMimk2COXB/xyh7Mnw7nJgFU0aRSbtX0vbvQz2suSzrQ6 -9mPKzan28JGoClqB0bw1vwf3VjjxHV2dgD57CmqFPv7kAC/2a56dE+etzXattZAL -+2JWTpmfQ0ePRRadtBm0VahQhnU8x0+jvAVrEawqpVW83ozYFyW/0WInM2J7jHgQ -16OosY4lj5L/DxpVxaArhRFoRfWPXfC37iE8Mou/I95isvPQIhp1wTo4jG0KM02B -oIVbp/QRNBQ6WtpOzvJs1gqQiJJTfqbKJXQ3NDEY9crpVS83HJ+Zv99PNsyNkFjG -QpU84U3ZhsI4ygjdY45mpZueqI1RVcRQdu8Hgvoo/78Q/Sir6gMGop3mVdVo2guI -kFcJrXh0Xk3ech4aVqrmKx/mPXGwOAQU0DAul4RW3fKg1QxQE7Tlw3+95Ee/+q5j -HARL0uDbCJpRO8Sl8NDEuL32n/2Ot6kQeCSHrU7KJRYAkTxkKvr8zNow7hFhHFPE -SnHvTnskI6noh0VY6NwMhmLvhm0wKkRxZPzUNc3sgLvbK1NymIZ9aKCZamzhZrmG -vnblEz/OSLwGUua465H3hM1vvBQiartj7+6ZqWIkSmBPABEBAAGJAjYEGAEKACAW -IQSgNcjBkhm6gh7OqGtk5ij41oRpbQUCWr5PIAIbIAAKCRBk5ij41oRpbWmeEACG -+axtDC8UoNp9ORiYwEWLzZWDuugE+ah7DYYGD4Vs633FXVZW3SgM/bFtJ/0Lg8CF -74jI4LMHyIjDzEjcoItwnhBLix+kUoJTvrY58GPydwekLuw1p4KXLqtRs4fsZbNQ -YTknl4jYtRWoxO98x7tun7Gq2gqmJkIB2uj630fKz5cBk6p6oDFKjzyrHe+V7BiK -3okQPaD4x7hq8OnTy7lOy92ZZAqztS4tNEb4DkYW1MpuwsJ7hbBZitc1siI+FVVb -GjVVGZz6ssXoW67Tz8+VxdWJxNLXlv27eMcj4sme5S0th/YYNA5fRRv6zuzqZAru -YNGLpYYU7JLvZJ+3lCwa5j5ycOGBF0GvsGs6gj6h+CHkjR/BgzAgWC+GgUgslt6q -aH04rWtV6rVz+Y91LcrX5P6OM4anmXD3Gp3kl35AypXb4KyASF19+11RUziD4Z7q -wQEWfbwOltNyZv2lD8s2jPr7P02axWRQUbZAEhxRmvOQev/FZPyCF6gqUo/HxRbQ -y3bzmnipyHSv1DlXNfCFCHvN8kGyZnRWARqIKRg+j9ediJgOUqlLhg6KmrTVxd5v -3Dfv52PW2UODDTM20s3cQGuX/UswzMRwPI/+P44iCMwEKdm7duM/5oisZT9Vhy7g -P15MreFZLcZvUVgjqgy0u57cstyGK1Bo9e2sFcK2fA== -=6Zb4 ------END PGP PUBLIC KEY BLOCK----- diff --git a/pgp_keys_combined.asc b/pgp_keys_combined.asc new file mode 100644 index 0000000..c23278e --- /dev/null +++ b/pgp_keys_combined.asc @@ -0,0 +1,1281 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFq+ToQBEADRYvIVtbK6owynD3j3nxwpW2KEk/p+aDvtXmc2SR2dBcZ8sFW2 +R5vEsG8d3/D3wgv5pcL3KfNNXQYUnXVbobrFUUWQYc79qIsE3MgiPf5NVOtwKPUR +i5g9YJgKvpBxkQfqp3LYGm9ZBtwo3DVLA3yn7KsazCmAgTNFJYw7ku1XxgmIzY6K +5J30DfbJiqDqj4f9GslCCCCH3qiPnuLG/HUyVLHMpbWlaiy9NI0GcaLxjJewHj9w +W2D2lydkxe5JGo7egUkV3ILcuLVSVKA35SKY27dYqfuyqp9tAzaRbjDYjsYdHA6G +BqrNrKBn/GwlFDPrVdcvN3ZSY2wMLTxWE3Axc/FweuHxFnou/80FwX7F3JD+oEQ6 +rofmcxOBCC7J98I7HZAhP9jBn88XIS2hztbLq8d6rZJZRtcz0k61VR0ddO+TrFmf +9rMYCPgCckRtVxeFIVIabrN1IzKynLFeo040h8hSGswd6YKDOVwjJY6Oa6EmVefZ +a8QSt4+M65RSzH6SEPY008F3nJUAK6MEkzTak+tFltZNrVWu8p2xd1j9nmxAwEhZ +/lgbxLqzYgaUWmfyHeZ8yVA0MhHzdiAL8nVUEdG3KecIq0RWCJLGLWWIjd6KAJl1 +yAmhRYKK/sjPDsL3elHsFACfZbyx3o5GGQNlas1FYoPLWbaNGaJtgFTF2QARAQAB +tCtQYWJsbyBHYWxpbmRvIFNhbGdhZG8gPHBhYmxvZ3NhbEBnbWFpbC5jb20+iQJO +BBMBCgA4FiEEoDXIwZIZuoIezqhrZOYo+NaEaW0FAlq+ToQCGwMFCwkIBwMFFQoJ +CAsFFgIDAQACHgECF4AACgkQZOYo+NaEaW2bmA/+PXIap2udLoUVOHxnsIBdqYwp +sv1Aj5lfIJmNhmxPbHShwp1Jg+w4urxe+2Dj5ofKVlIo1i83bQkvnKJMDXDVuc/K +P6zqhBJ3rT4Q3qx2mzX8bIfQoJ2JHuH4lkP+I7doDcHHRyeNASyk72VdQmU4twNw +Ibn8nSNV6ThKHdoPYzVnO2rZUFcGIqH5HNsvR+B7cc1MBCHsgURYwSVhSePIFGlZ +iasdBD6QQkDSe4QWi7AcJFWFElw4kbOKJWxAWsrEk+tMXJVGRjnmL289EmPCx/vx +BqKy7Mse0yWCSRR3vB+O6TB1S5SgEyEgqlYsfGNv1qf/rfRD4KkyCbNU3LhY1Aim +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 +=6Zb4 +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=baTu +-----END PGP PUBLIC KEY BLOCK----- diff --git a/python3.10.spec b/python3.10.spec index e4e3677..a1744fe 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.13 +%global general_version %{pybasever}.14 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 6%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -261,8 +261,13 @@ BuildRequires: python3-rpm-generators Source0: %{url}ftp/python/%{general_version}/Python-%{upstream_version}.tar.xz Source1: %{url}ftp/python/%{general_version}/Python-%{upstream_version}.tar.xz.asc -# The release manager for Python 3.10 is pablogsal -Source2: https://keybase.io/pablogsal/pgp_keys.asc +# The release manager for Python 3.10 is pablogsal, +# but Python 3.10.14 was signed by Łukasz Langa. +# pgp_keys_combined.asc contains keys of both release managers. +# Upstream issue: https://github.com/python/cpython/issues/117053 +# https://keybase.io/pablogsal/pgp_keys.asc +# https://keybase.io/ambv/pgp_keys.asc +Source2: pgp_keys_combined.asc # A simple script to check timestamps of bytecode files # Run in check section with Python that is currently being built @@ -329,13 +334,6 @@ Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-par # zlib-ng defines the version as "1.3.0.zlib-ng". Patch419: 00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch -# 00422 # a353cebef737c41420dc7ae2469dd657371b8881 -# gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 -# -# Feeding the parser by too small chunks defers parsing to prevent -# CVE-2023-52425. Future versions of Expat may be more reactive. -Patch422: 00422-gh-115133-fix-tests-for-xmlpullparser-with-expat-2-6-0.patch - # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1609,6 +1607,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Mar 20 2024 Tomáš Hrnčiar - 3.10.14-1 +- Update to 3.10.14 + * Wed Feb 28 2024 Charalampos Stratakis - 3.10.13-6 - Fix tests for XMLPullParser with Expat 2.6.0 diff --git a/sources b/sources index b4b0734..9acf607 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.13.tar.xz) = 7579772e501486b2b07f78142082dee1e99c7643640098860ac0cf2ca87daf7588b0c00b1db1960146b37f56a6ed98fd08297c25c9a19b612cf6e6a258984da8 -SHA512 (Python-3.10.13.tar.xz.asc) = 3083e66f8d26128302dc77a0c6ba3bfefc4229928a1bbd06460f2fec2421188bd30d493e3bce138cde1fed1df206e1dda04912b9f43a0b81229f1e69135e3a7f +SHA512 (Python-3.10.14.tar.xz) = adee638aeca898743da4b8245c0fa1e9b94b516f22e716e60c99038f0bb3dcbf726317aa86656404be1719b5a7c58eb09533720ebc0badbb04bd9a534dd48fef +SHA512 (Python-3.10.14.tar.xz.asc) = 821046b8eb559c3ef800f26739c995e7a71469a1e765ee7d516690236e16d2637ada7b2f9535b8c0dceaacf243e7b059f6f106b4a25faae365aa2099d1ff538c From b3ea7e1f7dcb3beb640a6619b8834cd53b611233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 17 Apr 2024 22:58:28 +0000 Subject: [PATCH 18/53] Require expat >= 2.6 to prevent errors when creating venvs with older expat The code in CPython uses XML_SetReparseDeferralEnabled when expat is >= 2.6 during the build. However, when users upgrade Python independently on the expat package, they may have expat 2.5 installed and see errors like: $ python3.1X -m venv venv Error: Command '['venv/bin/python3.1X', '-m', 'ensurepip', '--upgrade', '--default-pip']' returned non-zero exit status 1. $ venv/bin/python3.1X -m ensurepip --upgrade --default-pip Traceback (most recent call last): ... File "/tmp/.../pip-24.0-py3-none-any.whl/pip/_internal/commands/install.py", line 15, in from pip._internal.cli.req_command import ( ...<3 lines>... ) File "/tmp/.../pip-24.0-py3-none-any.whl/pip/_internal/cli/req_command.py", line 21, in from pip._internal.index.package_finder import PackageFinder File "/tmp/.../pip-24.0-py3-none-any.whl/pip/_internal/index/package_finder.py", line 30, in from pip._internal.req import InstallRequirement File "/tmp/.../pip-24.0-py3-none-any.whl/pip/_internal/req/__init__.py", line 8, in from .req_install import InstallRequirement File "/tmp/.../pip-24.0-py3-none-any.whl/pip/_internal/req/req_install.py", line 40, in from pip._internal.operations.install.wheel import install_wheel File "/tmp/.../pip-24.0-py3-none-any.whl/pip/_internal/operations/install/wheel.py", line 39, in from pip._vendor.distlib.scripts import ScriptMaker File "/tmp/.../pip-24.0-py3-none-any.whl/pip/_vendor/distlib/scripts.py", line 16, in from .compat import sysconfig, detect_encoding, ZipFile File "/tmp/.../pip-24.0-py3-none-any.whl/pip/_vendor/distlib/compat.py", line 81, in import xmlrpc.client as xmlrpclib File "/usr/lib64/python3.1X/xmlrpc/client.py", line 138, in from xml.parsers import expat File "/usr/lib64/python3.1X/xml/parsers/expat.py", line 4, in from pyexpat import * ImportError: /usr/lib64/python3.1X/lib-dynload/pyexpat.cpython-31X-x86_64-linux-gnu.so: undefined symbol: XML_SetReparseDeferralEnabled Traceback (most recent call last): ... subprocess.CalledProcessError: Command '['venv/bin/python3.1X', '-W', 'ignore::DeprecationWarning', '-c', '\nimport runpy\nimport sys\nsys.path = [\'/tmp/.../pip-24.0-py3-none-any.whl\'] + sys.path\nsys.argv[1:] = [\'install\', \'--no-cache-dir\', \'--no-index\', \'--find-links\', \'/tmp/...\', \'--upgrade\', \'pip\']\nrunpy.run_module("pip", run_name="__main__", alter_sys=True)\n']' returned non-zero exit status 1. Thanks to Markus Falb for discovering this problem. https://lists.fedoraproject.org/archives/list/python-devel@lists.fedoraproject.org/thread/7XHGWHBQDNFKNGSZTP44SSD6PQKZPG6C/ --- python3.10.spec | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index a1744fe..df16825 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -192,7 +192,8 @@ BuildRequires: bluez-libs-devel BuildRequires: bzip2 BuildRequires: bzip2-devel BuildRequires: desktop-file-utils -BuildRequires: expat-devel +# See the runtime requirement in the -libs subpackage +BuildRequires: expat-devel >= 2.6 BuildRequires: findutils BuildRequires: gcc-c++ @@ -479,6 +480,14 @@ 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 expat >= 2.6, but installed with older expat, we get: +# ImportError: /usr/lib64/python3.X/lib-dynload/pyexpat.cpython-....so: +# undefined symbol: XML_SetReparseDeferralEnabled +# This breaks many things, including python -m venv. +# Other subpackages (like -debug) also need this, but they all depend on -libs. +Requires: expat >= 2.6 + # Since patch 251 changed from distutils to sysconfig, pip needed to be adapted # The previous versions could cause serious bugs during `sudo pip install --upgrade ...` # Better safe than sorry @@ -1607,6 +1616,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Apr 17 2024 Miro Hrončok - 3.10.14-2 +- Require expat >= 2.6 to prevent errors when creating venvs with older expat + * Wed Mar 20 2024 Tomáš Hrnčiar - 3.10.14-1 - Update to 3.10.14 From f7d93eee1878711a65574c6ad46fc957b9e1ecf9 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 19 Jul 2024 17:26:02 +0000 Subject: [PATCH 19/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index df16825..d839f00 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -1616,6 +1616,9 @@ CheckPython optimized # ====================================================== %changelog +* Fri Jul 19 2024 Fedora Release Engineering - 3.10.14-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + * Wed Apr 17 2024 Miro Hrončok - 3.10.14-2 - Require expat >= 2.6 to prevent errors when creating venvs with older expat From 67b71083facd7607a7f04b067c017f82fc477287 Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Tue, 23 Jul 2024 13:23:31 +0200 Subject: [PATCH 20/53] Require systemtap-sdt-devel for sys/sdt.h --- python3.10.spec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index d839f00..c9283f0 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 4%{?dist} License: Python-2.0.1 @@ -236,6 +236,7 @@ BuildRequires: valgrind-devel BuildRequires: xz-devel BuildRequires: zlib-devel +BuildRequires: systemtap-sdt-devel BuildRequires: /usr/bin/dtrace # workaround http://bugs.python.org/issue19804 (test_uuid requires ifconfig) @@ -1616,6 +1617,9 @@ CheckPython optimized # ====================================================== %changelog +* Tue Jul 23 2024 Lumír Balhar - 3.10.14-4 +- Require systemtap-sdt-devel for sys/sdt.h + * Fri Jul 19 2024 Fedora Release Engineering - 3.10.14-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild From 6d6290fa95ba6b1e1242c107929643a068ba8689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Mon, 9 Sep 2024 10:54:57 +0200 Subject: [PATCH 21/53] Update to 3.10.15 --- ...-addresses-in-email-parseaddr-111116.patch | 500 ------------------ ...h-non-int-suffix-gh-112771-gh-112774.patch | 66 --- python3.10.spec | 22 +- sources | 4 +- 4 files changed, 7 insertions(+), 585 deletions(-) delete mode 100644 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch delete mode 100644 00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch diff --git a/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch b/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch deleted file mode 100644 index 7d2a813..0000000 --- a/00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch +++ /dev/null @@ -1,500 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Victor Stinner -Date: Fri, 15 Dec 2023 16:10:40 +0100 -Subject: [PATCH] 00415: [CVE-2023-27043] gh-102988: Reject malformed addresses - in email.parseaddr() (#111116) - -Detect email address parsing errors and return empty tuple to -indicate the parsing error (old API). Add an optional 'strict' -parameter to getaddresses() and parseaddr() functions. Patch by -Thomas Dwyer. - -Co-Authored-By: Thomas Dwyer ---- - Doc/library/email.utils.rst | 19 +- - Lib/email/utils.py | 151 ++++++++++++- - Lib/test/test_email/test_email.py | 204 +++++++++++++++++- - ...-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 + - 4 files changed, 361 insertions(+), 21 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst - -diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst -index 0e266b6a45..6723dc4f13 100644 ---- a/Doc/library/email.utils.rst -+++ b/Doc/library/email.utils.rst -@@ -60,13 +60,18 @@ of the new API. - begins with angle brackets, they are stripped off. - - --.. function:: parseaddr(address) -+.. function:: parseaddr(address, *, strict=True) - - Parse address -- which should be the value of some address-containing field such - as :mailheader:`To` or :mailheader:`Cc` -- into its constituent *realname* and - *email address* parts. Returns a tuple of that information, unless the parse - fails, in which case a 2-tuple of ``('', '')`` is returned. - -+ If *strict* is true, use a strict parser which rejects malformed inputs. -+ -+ .. versionchanged:: 3.13 -+ Add *strict* optional parameter and reject malformed inputs by default. -+ - - .. function:: formataddr(pair, charset='utf-8') - -@@ -84,12 +89,15 @@ of the new API. - Added the *charset* option. - - --.. function:: getaddresses(fieldvalues) -+.. function:: getaddresses(fieldvalues, *, strict=True) - - This method returns a list of 2-tuples of the form returned by ``parseaddr()``. - *fieldvalues* is a sequence of header field values as might be returned by -- :meth:`Message.get_all `. Here's a simple -- example that gets all the recipients of a message:: -+ :meth:`Message.get_all `. -+ -+ If *strict* is true, use a strict parser which rejects malformed inputs. -+ -+ Here's a simple example that gets all the recipients of a message:: - - from email.utils import getaddresses - -@@ -99,6 +107,9 @@ of the new API. - resent_ccs = msg.get_all('resent-cc', []) - all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) - -+ .. versionchanged:: 3.13 -+ Add *strict* optional parameter and reject malformed inputs by default. -+ - - .. function:: parsedate(date) - -diff --git a/Lib/email/utils.py b/Lib/email/utils.py -index cfdfeb3f1a..9522341fab 100644 ---- a/Lib/email/utils.py -+++ b/Lib/email/utils.py -@@ -48,6 +48,7 @@ - specialsre = re.compile(r'[][\\()<>@,:;".]') - escapesre = re.compile(r'[\\"]') - -+ - def _has_surrogates(s): - """Return True if s contains surrogate-escaped binary data.""" - # This check is based on the fact that unless there are surrogates, utf8 -@@ -106,12 +107,127 @@ def formataddr(pair, charset='utf-8'): - return address - - -+def _iter_escaped_chars(addr): -+ pos = 0 -+ escape = False -+ for pos, ch in enumerate(addr): -+ if escape: -+ yield (pos, '\\' + ch) -+ escape = False -+ elif ch == '\\': -+ escape = True -+ else: -+ yield (pos, ch) -+ if escape: -+ yield (pos, '\\') - --def getaddresses(fieldvalues): -- """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" -- all = COMMASPACE.join(str(v) for v in fieldvalues) -- a = _AddressList(all) -- return a.addresslist -+ -+def _strip_quoted_realnames(addr): -+ """Strip real names between quotes.""" -+ if '"' not in addr: -+ # Fast path -+ return addr -+ -+ start = 0 -+ open_pos = None -+ result = [] -+ for pos, ch in _iter_escaped_chars(addr): -+ if ch == '"': -+ if open_pos is None: -+ open_pos = pos -+ else: -+ if start != open_pos: -+ result.append(addr[start:open_pos]) -+ start = pos + 1 -+ open_pos = None -+ -+ if start < len(addr): -+ result.append(addr[start:]) -+ -+ return ''.join(result) -+ -+ -+supports_strict_parsing = True -+ -+def getaddresses(fieldvalues, *, strict=True): -+ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue. -+ -+ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in -+ its place. -+ -+ If strict is true, use a strict parser which rejects malformed inputs. -+ """ -+ -+ # If strict is true, if the resulting list of parsed addresses is greater -+ # than the number of fieldvalues in the input list, a parsing error has -+ # occurred and consequently a list containing a single empty 2-tuple [('', -+ # '')] is returned in its place. This is done to avoid invalid output. -+ # -+ # Malformed input: getaddresses(['alice@example.com ']) -+ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')] -+ # Safe output: [('', '')] -+ -+ if not strict: -+ all = COMMASPACE.join(str(v) for v in fieldvalues) -+ a = _AddressList(all) -+ return a.addresslist -+ -+ fieldvalues = [str(v) for v in fieldvalues] -+ fieldvalues = _pre_parse_validation(fieldvalues) -+ addr = COMMASPACE.join(fieldvalues) -+ a = _AddressList(addr) -+ result = _post_parse_validation(a.addresslist) -+ -+ # Treat output as invalid if the number of addresses is not equal to the -+ # expected number of addresses. -+ n = 0 -+ for v in fieldvalues: -+ # When a comma is used in the Real Name part it is not a deliminator. -+ # So strip those out before counting the commas. -+ v = _strip_quoted_realnames(v) -+ # Expected number of addresses: 1 + number of commas -+ n += 1 + v.count(',') -+ if len(result) != n: -+ return [('', '')] -+ -+ return result -+ -+ -+def _check_parenthesis(addr): -+ # Ignore parenthesis in quoted real names. -+ addr = _strip_quoted_realnames(addr) -+ -+ opens = 0 -+ for pos, ch in _iter_escaped_chars(addr): -+ if ch == '(': -+ opens += 1 -+ elif ch == ')': -+ opens -= 1 -+ if opens < 0: -+ return False -+ return (opens == 0) -+ -+ -+def _pre_parse_validation(email_header_fields): -+ accepted_values = [] -+ for v in email_header_fields: -+ if not _check_parenthesis(v): -+ v = "('', '')" -+ accepted_values.append(v) -+ -+ return accepted_values -+ -+ -+def _post_parse_validation(parsed_email_header_tuples): -+ accepted_values = [] -+ # The parser would have parsed a correctly formatted domain-literal -+ # The existence of an [ after parsing indicates a parsing failure -+ for v in parsed_email_header_tuples: -+ if '[' in v[1]: -+ v = ('', '') -+ accepted_values.append(v) -+ -+ return accepted_values - - - def _format_timetuple_and_zone(timetuple, zone): -@@ -205,16 +321,33 @@ def parsedate_to_datetime(data): - tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) - - --def parseaddr(addr): -+def parseaddr(addr, *, strict=True): - """ - Parse addr into its constituent realname and email address parts. - - Return a tuple of realname and email address, unless the parse fails, in - which case return a 2-tuple of ('', ''). -+ -+ If strict is True, use a strict parser which rejects malformed inputs. - """ -- addrs = _AddressList(addr).addresslist -- if not addrs: -- return '', '' -+ if not strict: -+ addrs = _AddressList(addr).addresslist -+ if not addrs: -+ return ('', '') -+ return addrs[0] -+ -+ if isinstance(addr, list): -+ addr = addr[0] -+ -+ if not isinstance(addr, str): -+ return ('', '') -+ -+ addr = _pre_parse_validation([addr])[0] -+ addrs = _post_parse_validation(_AddressList(addr).addresslist) -+ -+ if not addrs or len(addrs) > 1: -+ return ('', '') -+ - return addrs[0] - - -diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py -index 8b16cca9bf..5b19bb38f6 100644 ---- a/Lib/test/test_email/test_email.py -+++ b/Lib/test/test_email/test_email.py -@@ -16,6 +16,7 @@ - - import email - import email.policy -+import email.utils - - from email.charset import Charset - from email.generator import Generator, DecodedGenerator, BytesGenerator -@@ -3288,15 +3289,154 @@ def test_getaddresses(self): - [('Al Person', 'aperson@dom.ain'), - ('Bud Person', 'bperson@dom.ain')]) - -+ def test_getaddresses_comma_in_name(self): -+ """GH-106669 regression test.""" -+ self.assertEqual( -+ utils.getaddresses( -+ [ -+ '"Bud, Person" ', -+ 'aperson@dom.ain (Al Person)', -+ '"Mariusz Felisiak" ', -+ ] -+ ), -+ [ -+ ('Bud, Person', 'bperson@dom.ain'), -+ ('Al Person', 'aperson@dom.ain'), -+ ('Mariusz Felisiak', 'to@example.com'), -+ ], -+ ) -+ -+ def test_parsing_errors(self): -+ """Test for parsing errors from CVE-2023-27043 and CVE-2019-16056""" -+ alice = 'alice@example.org' -+ bob = 'bob@example.com' -+ empty = ('', '') -+ -+ # Test utils.getaddresses() and utils.parseaddr() on malformed email -+ # addresses: default behavior (strict=True) rejects malformed address, -+ # and strict=False which tolerates malformed address. -+ for invalid_separator, expected_non_strict in ( -+ ('(', [(f'<{bob}>', alice)]), -+ (')', [('', alice), empty, ('', bob)]), -+ ('<', [('', alice), empty, ('', bob), empty]), -+ ('>', [('', alice), empty, ('', bob)]), -+ ('[', [('', f'{alice}[<{bob}>]')]), -+ (']', [('', alice), empty, ('', bob)]), -+ ('@', [empty, empty, ('', bob)]), -+ (';', [('', alice), empty, ('', bob)]), -+ (':', [('', alice), ('', bob)]), -+ ('.', [('', alice + '.'), ('', bob)]), -+ ('"', [('', alice), ('', f'<{bob}>')]), -+ ): -+ address = f'{alice}{invalid_separator}<{bob}>' -+ with self.subTest(address=address): -+ self.assertEqual(utils.getaddresses([address]), -+ [empty]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ expected_non_strict) -+ -+ self.assertEqual(utils.parseaddr([address]), -+ empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Comma (',') is treated differently depending on strict parameter. -+ # Comma without quotes. -+ address = f'{alice},<{bob}>' -+ self.assertEqual(utils.getaddresses([address]), -+ [('', alice), ('', bob)]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('', alice), ('', bob)]) -+ self.assertEqual(utils.parseaddr([address]), -+ empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Real name between quotes containing comma. -+ address = '"Alice, alice@example.org" ' -+ expected_strict = ('Alice, alice@example.org', 'bob@example.com') -+ self.assertEqual(utils.getaddresses([address]), [expected_strict]) -+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) -+ self.assertEqual(utils.parseaddr([address]), expected_strict) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Valid parenthesis in comments. -+ address = 'alice@example.org (Alice)' -+ expected_strict = ('Alice', 'alice@example.org') -+ self.assertEqual(utils.getaddresses([address]), [expected_strict]) -+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict]) -+ self.assertEqual(utils.parseaddr([address]), expected_strict) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Invalid parenthesis in comments. -+ address = 'alice@example.org )Alice(' -+ self.assertEqual(utils.getaddresses([address]), [empty]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('', 'alice@example.org'), ('', ''), ('', 'Alice')]) -+ self.assertEqual(utils.parseaddr([address]), empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Two addresses with quotes separated by comma. -+ address = '"Jane Doe" , "John Doe" ' -+ self.assertEqual(utils.getaddresses([address]), -+ [('Jane Doe', 'jane@example.net'), -+ ('John Doe', 'john@example.net')]) -+ self.assertEqual(utils.getaddresses([address], strict=False), -+ [('Jane Doe', 'jane@example.net'), -+ ('John Doe', 'john@example.net')]) -+ self.assertEqual(utils.parseaddr([address]), empty) -+ self.assertEqual(utils.parseaddr([address], strict=False), -+ ('', address)) -+ -+ # Test email.utils.supports_strict_parsing attribute -+ self.assertEqual(email.utils.supports_strict_parsing, True) -+ - def test_getaddresses_nasty(self): -- eq = self.assertEqual -- eq(utils.getaddresses(['foo: ;']), [('', '')]) -- eq(utils.getaddresses( -- ['[]*-- =~$']), -- [('', ''), ('', ''), ('', '*--')]) -- eq(utils.getaddresses( -- ['foo: ;', '"Jason R. Mastaler" ']), -- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) -+ for addresses, expected in ( -+ (['"Sürname, Firstname" '], -+ [('Sürname, Firstname', 'to@example.com')]), -+ -+ (['foo: ;'], -+ [('', '')]), -+ -+ (['foo: ;', '"Jason R. Mastaler" '], -+ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]), -+ -+ ([r'Pete(A nice \) chap) '], -+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]), -+ -+ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'], -+ [('', '')]), -+ -+ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'], -+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]), -+ -+ (['John Doe '], -+ [('John Doe (comment)', 'jdoe@machine.example')]), -+ -+ (['"Mary Smith: Personal Account" '], -+ [('Mary Smith: Personal Account', 'smith@home.example')]), -+ -+ (['Undisclosed recipients:;'], -+ [('', '')]), -+ -+ ([r', "Giant; \"Big\" Box" '], -+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]), -+ ): -+ with self.subTest(addresses=addresses): -+ self.assertEqual(utils.getaddresses(addresses), -+ expected) -+ self.assertEqual(utils.getaddresses(addresses, strict=False), -+ expected) -+ -+ addresses = ['[]*-- =~$'] -+ self.assertEqual(utils.getaddresses(addresses), -+ [('', '')]) -+ self.assertEqual(utils.getaddresses(addresses, strict=False), -+ [('', ''), ('', ''), ('', '*--')]) - - def test_getaddresses_embedded_comment(self): - """Test proper handling of a nested comment""" -@@ -3485,6 +3625,54 @@ def test_mime_classes_policy_argument(self): - m = cls(*constructor, policy=email.policy.default) - self.assertIs(m.policy, email.policy.default) - -+ def test_iter_escaped_chars(self): -+ self.assertEqual(list(utils._iter_escaped_chars(r'a\\b\"c\\"d')), -+ [(0, 'a'), -+ (2, '\\\\'), -+ (3, 'b'), -+ (5, '\\"'), -+ (6, 'c'), -+ (8, '\\\\'), -+ (9, '"'), -+ (10, 'd')]) -+ self.assertEqual(list(utils._iter_escaped_chars('a\\')), -+ [(0, 'a'), (1, '\\')]) -+ -+ def test_strip_quoted_realnames(self): -+ def check(addr, expected): -+ self.assertEqual(utils._strip_quoted_realnames(addr), expected) -+ -+ check('"Jane Doe" , "John Doe" ', -+ ' , ') -+ check(r'"Jane \"Doe\"." ', -+ ' ') -+ -+ # special cases -+ check(r'before"name"after', 'beforeafter') -+ check(r'before"name"', 'before') -+ check(r'b"name"', 'b') # single char -+ check(r'"name"after', 'after') -+ check(r'"name"a', 'a') # single char -+ check(r'"name"', '') -+ -+ # no change -+ for addr in ( -+ 'Jane Doe , John Doe ', -+ 'lone " quote', -+ ): -+ self.assertEqual(utils._strip_quoted_realnames(addr), addr) -+ -+ -+ def test_check_parenthesis(self): -+ addr = 'alice@example.net' -+ self.assertTrue(utils._check_parenthesis(f'{addr} (Alice)')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} )Alice(')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} (Alice))')) -+ self.assertFalse(utils._check_parenthesis(f'{addr} ((Alice)')) -+ -+ # Ignore real name between quotes -+ self.assertTrue(utils._check_parenthesis(f'")Alice((" {addr}')) -+ - - # Test the iterator/generators - class TestIterators(TestEmailBase): -diff --git a/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst -new file mode 100644 -index 0000000000..3d0e9e4078 ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst -@@ -0,0 +1,8 @@ -+:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now -+return ``('', '')`` 2-tuples in more situations where invalid email -+addresses are encountered instead of potentially inaccurate values. Add -+optional *strict* parameter to these two functions: use ``strict=False`` to -+get the old behavior, accept malformed inputs. -+``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check -+if the *strict* paramater is available. Patch by Thomas Dwyer and Victor -+Stinner to improve the CVE-2023-27043 fix. diff --git a/00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch b/00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch deleted file mode 100644 index 1e718a6..0000000 --- a/00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= -Date: Tue, 5 Dec 2023 21:02:06 +0100 -Subject: [PATCH] 00419: gh-112769: test_zlib: Fix comparison of - ZLIB_RUNTIME_VERSION with non-int suffix (GH-112771) (GH-112774) - -zlib-ng defines the version as "1.3.0.zlib-ng". -(cherry picked from commit d384813ff18b33280a90b6d2011654528a2b6ad1) ---- - Lib/test/test_zlib.py | 28 ++++++++++++++++------------ - 1 file changed, 16 insertions(+), 12 deletions(-) - -diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py -index cb0610837b..98cac4fb91 100644 ---- a/Lib/test/test_zlib.py -+++ b/Lib/test/test_zlib.py -@@ -19,6 +19,20 @@ - 'requires Decompress.copy()') - - -+def _zlib_runtime_version_tuple(zlib_version=zlib.ZLIB_RUNTIME_VERSION): -+ # Register "1.2.3" as "1.2.3.0" -+ # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux" -+ v = zlib_version.split('-', 1)[0].split('.') -+ if len(v) < 4: -+ v.append('0') -+ elif not v[-1].isnumeric(): -+ v[-1] = '0' -+ return tuple(map(int, v)) -+ -+ -+ZLIB_RUNTIME_VERSION_TUPLE = _zlib_runtime_version_tuple() -+ -+ - class VersionTestCase(unittest.TestCase): - - def test_library_version(self): -@@ -445,9 +459,8 @@ def test_flushes(self): - sync_opt = ['Z_NO_FLUSH', 'Z_SYNC_FLUSH', 'Z_FULL_FLUSH', - 'Z_PARTIAL_FLUSH'] - -- ver = tuple(int(v) for v in zlib.ZLIB_RUNTIME_VERSION.split('.')) - # Z_BLOCK has a known failure prior to 1.2.5.3 -- if ver >= (1, 2, 5, 3): -+ if ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 5, 3): - sync_opt.append('Z_BLOCK') - - sync_opt = [getattr(zlib, opt) for opt in sync_opt -@@ -776,16 +789,7 @@ def test_large_unconsumed_tail(self, size): - - def test_wbits(self): - # wbits=0 only supported since zlib v1.2.3.5 -- # Register "1.2.3" as "1.2.3.0" -- # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux" -- v = zlib.ZLIB_RUNTIME_VERSION.split('-', 1)[0].split('.') -- if len(v) < 4: -- v.append('0') -- elif not v[-1].isnumeric(): -- v[-1] = '0' -- -- v = tuple(map(int, v)) -- supports_wbits_0 = v >= (1, 2, 3, 5) -+ supports_wbits_0 = ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 3, 5) - - co = zlib.compressobj(level=1, wbits=15) - zlib15 = co.compress(HAMLET_SCENE) + co.flush() diff --git a/python3.10.spec b/python3.10.spec index c9283f0..9841543 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.14 +%global general_version %{pybasever}.15 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 4%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -321,21 +321,6 @@ Patch251: 00251-change-user-install-location.patch # https://github.com/GrahamDumpleton/mod_wsgi/issues/730 Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch -# 00415 # 9ca4533e0b4a03d919953017026f66c6a060756e -# [CVE-2023-27043] gh-102988: Reject malformed addresses in email.parseaddr() (#111116) -# -# Detect email address parsing errors and return empty tuple to -# indicate the parsing error (old API). Add an optional 'strict' -# parameter to getaddresses() and parseaddr() functions. Patch by -# Thomas Dwyer. -Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch - -# 00419 # f13682530cc7e4daec2e40acd56508846fdd3aad -# gh-112769: test_zlib: Fix comparison of ZLIB_RUNTIME_VERSION with non-int suffix (GH-112771) (GH-112774) -# -# zlib-ng defines the version as "1.3.0.zlib-ng". -Patch419: 00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch - # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1617,6 +1602,9 @@ CheckPython optimized # ====================================================== %changelog +* Mon Sep 09 2024 Tomáš Hrnčiar - 3.10.15-1 +- Update to 3.10.15 + * Tue Jul 23 2024 Lumír Balhar - 3.10.14-4 - Require systemtap-sdt-devel for sys/sdt.h diff --git a/sources b/sources index 9acf607..1859df7 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.14.tar.xz) = adee638aeca898743da4b8245c0fa1e9b94b516f22e716e60c99038f0bb3dcbf726317aa86656404be1719b5a7c58eb09533720ebc0badbb04bd9a534dd48fef -SHA512 (Python-3.10.14.tar.xz.asc) = 821046b8eb559c3ef800f26739c995e7a71469a1e765ee7d516690236e16d2637ada7b2f9535b8c0dceaacf243e7b059f6f106b4a25faae365aa2099d1ff538c +SHA512 (Python-3.10.15.tar.xz) = 3c0396b2dae1521315a9b5ce9ad4ab6ede385447922fceb00ea3df75bfd79cb547b9380e936d83c9c1b964e38e16d70e34aec39f4c292853a032407bb6a58063 +SHA512 (Python-3.10.15.tar.xz.asc) = 0cac9b78649330a2a35a835d45e4a8a111db45943cc0ae0d24f5d9e03a058b4b739fe43d6cd1aee481dcd415a57820bf875996fa60b81d4d5911588db5adad70 From cdabc2abe703488c5d24475c2a876b9b845fe613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 9 Sep 2024 16:10:38 +0200 Subject: [PATCH 22/53] Skip test_sendfile_close_peer_in_the_middle_of_receiving on ppc64le Reported upstream https://github.com/python/cpython/issues/123384 Which was a duplicate of https://github.com/python/cpython/issues/120226 The test is fixed on the main branch, but skipping is easier than backporting. The failure occurs with Kernel 6.10.x even when Python is built with an older Kernel. --- python3.10.spec | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python3.10.spec b/python3.10.spec index 9841543..d302ccf 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -1095,6 +1095,8 @@ CheckPython() { # test_distutils # distutils.tests.test_bdist_rpm tests fail when bootstraping the Python # package: rpmbuild requires /usr/bin/pythonX.Y to be installed + # test_sendfile_close_peer_in_the_middle_of_receiving: + # https://github.com/python/cpython/issues/120226 LD_LIBRARY_PATH=$ConfDir $ConfDir/python -m test.regrtest \ -wW --slowest -j0 --timeout=1800 \ %if %{with bootstrap} @@ -1103,6 +1105,9 @@ CheckPython() { %ifarch %{mips64} -x test_ctypes \ %endif + %ifarch ppc64le + -i test_sendfile_close_peer_in_the_middle_of_receiving \ + %endif echo FINISHED: CHECKING OF PYTHON FOR CONFIGURATION: $ConfName From 5c73fd0f8e44b537b1291b9f1894bb5f7eb9143b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 11 Sep 2024 20:20:50 +0200 Subject: [PATCH 23/53] Fix ThreadedVSOCKSocketStreamTest --- ...treamtest-gh-119465-gh-119479-119484.patch | 66 +++++++++++++++++++ python3.10.spec | 16 ++++- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch diff --git a/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch b/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch new file mode 100644 index 0000000..7e1c35d --- /dev/null +++ b/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch @@ -0,0 +1,66 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Fri, 24 May 2024 01:23:55 +0200 +Subject: [PATCH] 00438: Fix ThreadedVSOCKSocketStreamTest (GH-119465) + (GH-119479) (#119484) + +Fix ThreadedVSOCKSocketStreamTest: if get_cid() returns the host +address or the "any" address, use the local communication address +(loopback): VMADDR_CID_LOCAL. + +On Linux 6.9, apparently, the /dev/vsock device is now available but +get_cid() returns VMADDR_CID_ANY (-1). + +(cherry picked from commit c750061047ee520d8299334df4b112fd983d7e48) + +Co-authored-by: Victor Stinner +(cherry picked from commit e94dbe4ed83460f18bd72563c5f09f6cdc71f604) + +Co-authored-by: Victor Stinner +--- + Lib/test/test_socket.py | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py +index 4c8f55d3e1..2acf608123 100644 +--- a/Lib/test/test_socket.py ++++ b/Lib/test/test_socket.py +@@ -41,6 +41,7 @@ + # test unicode string and carriage return + MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') + ++VMADDR_CID_LOCAL = 1 + VSOCKPORT = 1234 + AIX = platform.system() == "AIX" + +@@ -124,8 +125,8 @@ def _have_socket_qipcrtr(): + + def _have_socket_vsock(): + """Check whether AF_VSOCK sockets are supported on this host.""" +- ret = get_cid() is not None +- return ret ++ cid = get_cid() ++ return (cid is not None) + + + def _have_socket_bluetooth(): +@@ -487,8 +488,6 @@ def clientTearDown(self): + @unittest.skipIf(fcntl is None, "need fcntl") + @unittest.skipUnless(HAVE_SOCKET_VSOCK, + 'VSOCK sockets required for this test.') +-@unittest.skipUnless(get_cid() != 2, +- "This test can only be run on a virtual guest.") + class ThreadedVSOCKSocketStreamTest(unittest.TestCase, ThreadableTest): + + def __init__(self, methodName='runTest'): +@@ -509,6 +508,9 @@ def clientSetUp(self): + self.cli = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) + self.addCleanup(self.cli.close) + cid = get_cid() ++ if cid in (socket.VMADDR_CID_HOST, socket.VMADDR_CID_ANY): ++ # gh-119461: Use the local communication address (loopback) ++ cid = VMADDR_CID_LOCAL + self.cli.connect((cid, VSOCKPORT)) + + def testStream(self): diff --git a/python3.10.spec b/python3.10.spec index d302ccf..8623a54 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -321,6 +321,17 @@ Patch251: 00251-change-user-install-location.patch # https://github.com/GrahamDumpleton/mod_wsgi/issues/730 Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch +# 00438 # 640f507108d102da99fa2f39d268a43f86c97acb +# Fix ThreadedVSOCKSocketStreamTest (GH-119465) (GH-119479) (#119484) +# +# Fix ThreadedVSOCKSocketStreamTest: if get_cid() returns the host +# address or the "any" address, use the local communication address +# (loopback): VMADDR_CID_LOCAL. +# +# On Linux 6.9, apparently, the /dev/vsock device is now available but +# get_cid() returns VMADDR_CID_ANY (-1). +Patch438: 00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1607,6 +1618,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Sep 11 2024 Miro Hrončok - 3.10.15-2 +- Fix ThreadedVSOCKSocketStreamTest + * Mon Sep 09 2024 Tomáš Hrnčiar - 3.10.15-1 - Update to 3.10.15 From 9af4582cf940c5e79027a4ccea9c8f33c53b4428 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Thu, 5 Dec 2024 00:54:17 +0100 Subject: [PATCH 24/53] Update to 3.10.16 Security fix for CVE-2024-9287 Resolves: rhbz#2321654 --- python3.10.spec | 9 +++++++-- sources | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index 8623a54..2a9e28a 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.15 +%global general_version %{pybasever}.16 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -1618,6 +1618,11 @@ CheckPython optimized # ====================================================== %changelog +* Wed Dec 04 2024 Charalampos Stratakis - 3.10.16-1 +- Update to 3.10.16 +- Security fix for CVE-2024-9287 +Resolves: rhbz#2321654 + * Wed Sep 11 2024 Miro Hrončok - 3.10.15-2 - Fix ThreadedVSOCKSocketStreamTest diff --git a/sources b/sources index 1859df7..fe5766d 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.15.tar.xz) = 3c0396b2dae1521315a9b5ce9ad4ab6ede385447922fceb00ea3df75bfd79cb547b9380e936d83c9c1b964e38e16d70e34aec39f4c292853a032407bb6a58063 -SHA512 (Python-3.10.15.tar.xz.asc) = 0cac9b78649330a2a35a835d45e4a8a111db45943cc0ae0d24f5d9e03a058b4b739fe43d6cd1aee481dcd415a57820bf875996fa60b81d4d5911588db5adad70 +SHA512 (Python-3.10.16.tar.xz) = a6b004974db93f21590f00b9b1a569dfd8c3e334f8c6a8eb8997dec5646fa4515cfc02a7dabea9e4f75f908938dc64333ce4899b9a26aff97f9ffcb027fa2bca +SHA512 (Python-3.10.16.tar.xz.asc) = 18032fdb60a022d6381b1137789ada1c4bb198049616ed158fb548e728dd1fb95d116a86c41c852ca2acc9127baf3cdd9644a1e2df90ae9c3ffcd66eabf454d6 From 10df3778231e849edb5856da80edef71d199b607 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 18 Jan 2025 21:08:05 +0000 Subject: [PATCH 25/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 2a9e28a..ee9c961 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -1618,6 +1618,9 @@ CheckPython optimized # ====================================================== %changelog +* Sat Jan 18 2025 Fedora Release Engineering - 3.10.16-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + * Wed Dec 04 2024 Charalampos Stratakis - 3.10.16-1 - Update to 3.10.16 - Security fix for CVE-2024-9287 From df15baee4759bd389c5b685ca502f489845b7da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Sat, 1 Feb 2025 19:56:54 +0100 Subject: [PATCH 26/53] Add explicit BR: libxcrypt-devel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Esser --- python3.10.spec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index ee9c961..0a4cb85 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -211,6 +211,7 @@ BuildRequires: libnsl2-devel BuildRequires: libtirpc-devel BuildRequires: libGL-devel BuildRequires: libuuid-devel +BuildRequires: libxcrypt-devel BuildRequires: libX11-devel BuildRequires: make BuildRequires: mpdecimal-devel @@ -1618,6 +1619,9 @@ CheckPython optimized # ====================================================== %changelog +* Sat Feb 01 2025 Björn Esser - 3.10.16-3 +- Add explicit BR: libxcrypt-devel + * Sat Jan 18 2025 Fedora Release Engineering - 3.10.16-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild From dfe7d5095710e43837ada19faf7d1767c6e114f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 3 Feb 2025 11:13:44 +0000 Subject: [PATCH 27/53] Ensure this package is built with Tk 8 - Fixes: rhbz#2337757 --- python3.10.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index 0a4cb85..056d369 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -225,9 +225,9 @@ BuildRequires: sqlite-devel BuildRequires: gdb BuildRequires: tar -BuildRequires: tcl-devel +BuildRequires: tcl-devel < 1:9 BuildRequires: tix-devel -BuildRequires: tk-devel +BuildRequires: tk-devel < 1:9 BuildRequires: tzdata %if %{with valgrind} From ee2c642d121163c701921884080baa51d0fba9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 6 Feb 2025 12:04:28 +0100 Subject: [PATCH 28/53] Rebuilt with mpdecimal 4.0.0 --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 056d369..06ef873 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 4%{?dist} License: Python-2.0.1 @@ -1619,6 +1619,9 @@ CheckPython optimized # ====================================================== %changelog +* Thu Feb 06 2025 Miro Hrončok - 3.10.16-4 +- Rebuilt with mpdecimal 4.0.0 + * Sat Feb 01 2025 Björn Esser - 3.10.16-3 - Add explicit BR: libxcrypt-devel From df9826a90ed6c7ee343c716ddac9440ca7998bae Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Mon, 10 Feb 2025 23:40:17 +0100 Subject: [PATCH 29/53] Security fix for CVE-2025-0938 Fixes: rhbz#2343276 --- ...treamtest-gh-119465-gh-119479-119484.patch | 2 +- ...-and-in-domain-names-for-parsed-urls.patch | 119 ++++++++++++++++++ python3.10.spec | 10 +- 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch diff --git a/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch b/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch index 7e1c35d..01a308d 100644 --- a/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch +++ b/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch @@ -23,7 +23,7 @@ Co-authored-by: Victor Stinner 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 4c8f55d3e1..2acf608123 100644 +index 0050298b41..12b90dcacb 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -41,6 +41,7 @@ diff --git a/00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch b/00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch new file mode 100644 index 0000000..5d005cb --- /dev/null +++ b/00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch @@ -0,0 +1,119 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Seth Michael Larson +Date: Fri, 31 Jan 2025 11:41:34 -0600 +Subject: [PATCH] 00450: CVE-2025-0938: Disallow square brackets ([ and ]) in + domain names for parsed URLs + +Co-authored-by: Peter Bierma +--- + Lib/test/test_urlparse.py | 37 ++++++++++++++++++- + Lib/urllib/parse.py | 20 +++++++++- + ...-01-28-14-08-03.gh-issue-105704.EnhHxu.rst | 4 ++ + 3 files changed, 58 insertions(+), 3 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst + +diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py +index f2ffc452e5..280644ef0b 100644 +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -1149,16 +1149,51 @@ def test_invalid_bracketed_hosts(self): + self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a1') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a1') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:1a') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:1a') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:/') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:?') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@prefix.[v6a.ip]') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@[v6a.ip].suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip]') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip[') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip].suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip[suffix') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip') ++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[suffix') + + def test_splitting_bracketed_hosts(self): +- p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query') ++ p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]:1234/path?query') + self.assertEqual(p1.hostname, 'v6a.ip') + self.assertEqual(p1.username, 'user') + self.assertEqual(p1.path, '/path') ++ self.assertEqual(p1.port, 1234) + p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query') + self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test') + self.assertEqual(p2.username, 'user') + self.assertEqual(p2.path, '/path') ++ self.assertIs(p2.port, None) + p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query') + self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test') + self.assertEqual(p3.username, 'user') +diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py +index 07e3248504..e1ee36d98e 100644 +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -442,6 +442,23 @@ def _checknetloc(netloc): + raise ValueError("netloc '" + netloc + "' contains invalid " + + "characters under NFKC normalization") + ++def _check_bracketed_netloc(netloc): ++ # Note that this function must mirror the splitting ++ # done in NetlocResultMixins._hostinfo(). ++ hostname_and_port = netloc.rpartition('@')[2] ++ before_bracket, have_open_br, bracketed = hostname_and_port.partition('[') ++ if have_open_br: ++ # No data is allowed before a bracket. ++ if before_bracket: ++ raise ValueError("Invalid IPv6 URL") ++ hostname, _, port = bracketed.partition(']') ++ # No data is allowed after the bracket but before the port delimiter. ++ if port and not port.startswith(":"): ++ raise ValueError("Invalid IPv6 URL") ++ else: ++ hostname, _, port = hostname_and_port.partition(':') ++ _check_bracketed_host(hostname) ++ + # Valid bracketed hosts are defined in + # https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/ + def _check_bracketed_host(hostname): +@@ -505,8 +522,7 @@ def urlsplit(url, scheme='', allow_fragments=True): + (']' in netloc and '[' not in netloc)): + raise ValueError("Invalid IPv6 URL") + if '[' in netloc and ']' in netloc: +- bracketed_host = netloc.partition('[')[2].partition(']')[0] +- _check_bracketed_host(bracketed_host) ++ _check_bracketed_netloc(netloc) + if allow_fragments and '#' in url: + url, fragment = url.split('#', 1) + if '?' in url: +diff --git a/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst +new file mode 100644 +index 0000000000..bff1bc6b0d +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst +@@ -0,0 +1,4 @@ ++When using :func:`urllib.parse.urlsplit` and :func:`urllib.parse.urlparse` host ++parsing would not reject domain names containing square brackets (``[`` and ++``]``). Square brackets are only valid for IPv6 and IPvFuture hosts according to ++`RFC 3986 Section 3.2.2 `__. diff --git a/python3.10.spec b/python3.10.spec index 06ef873..15b0da5 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 4%{?dist} +Release: 5%{?dist} License: Python-2.0.1 @@ -333,6 +333,10 @@ Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-g # get_cid() returns VMADDR_CID_ANY (-1). Patch438: 00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch +# 00450 # 4ab8663661748eb994c09e4ae89f59eb84c5d3ea +# CVE-2025-0938: Disallow square brackets ([ and ]) in domain names for parsed URLs +Patch450: 00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1619,6 +1623,10 @@ CheckPython optimized # ====================================================== %changelog +* Mon Feb 10 2025 Charalampos Stratakis - 3.10.16-5 +- Security fix for CVE-2025-0938 +- Fixes: rhbz#2343276 + * Thu Feb 06 2025 Miro Hrončok - 3.10.16-4 - Rebuilt with mpdecimal 4.0.0 From aceb601ce349562dce8140241fec105d0b3ecdee Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Tue, 1 Apr 2025 01:44:48 +0200 Subject: [PATCH 30/53] Properly apply exported CFLAGS for dtrace/systemtap builds Fixes: rhbz#2356303 --- ...d-cflags-for-dtrace-systemtap-builds.patch | 52 +++++++++++++++++++ python3.10.spec | 18 ++++++- 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch diff --git a/00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch b/00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch new file mode 100644 index 0000000..359d9ac --- /dev/null +++ b/00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Mon, 31 Mar 2025 20:29:04 +0200 +Subject: [PATCH] 00452: Properly apply exported CFLAGS for dtrace/systemtap + builds + +When using --with-dtrace the resulting object file could be missing +specific CFLAGS exported by the build system due to the systemtap +script using specific defaults. + +Exporting the CC and CFLAGS variables before the dtrace invocation +allows us to properly apply CFLAGS exported by the build system +even when cross-compiling. + +Co-authored-by: stratakis +--- + Makefile.pre.in | 4 ++-- + .../next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst | 2 ++ + 2 files changed, 4 insertions(+), 2 deletions(-) + create mode 100644 Misc/NEWS.d/next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst + +diff --git a/Makefile.pre.in b/Makefile.pre.in +index fa99dd86c4..e9ba702a5c 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -1001,7 +1001,7 @@ Python/frozen.o: $(srcdir)/Python/importlib.h $(srcdir)/Python/importlib_externa + # an include guard, so we can't use a pipeline to transform its output. + Include/pydtrace_probes.h: $(srcdir)/Include/pydtrace.d + $(MKDIR_P) Include +- $(DTRACE) $(DFLAGS) -o $@ -h -s $< ++ CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -h -s $< + : sed in-place edit with POSIX-only tools + sed 's/PYTHON_/PyDTrace_/' $@ > $@.tmp + mv $@.tmp $@ +@@ -1011,7 +1011,7 @@ Python/import.o: $(srcdir)/Include/pydtrace.h + Modules/gcmodule.o: $(srcdir)/Include/pydtrace.h + + Python/pydtrace.o: $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS) +- $(DTRACE) $(DFLAGS) -o $@ -G -s $< $(DTRACE_DEPS) ++ CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -G -s $< $(DTRACE_DEPS) + + Objects/typeobject.o: Objects/typeslots.inc + +diff --git a/Misc/NEWS.d/next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst b/Misc/NEWS.d/next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst +new file mode 100644 +index 0000000000..a287e0b228 +--- /dev/null ++++ b/Misc/NEWS.d/next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst +@@ -0,0 +1,2 @@ ++The DTrace build now properly passes the ``CC`` and ``CFLAGS`` variables ++to the ``dtrace`` command when utilizing SystemTap on Linux. diff --git a/python3.10.spec b/python3.10.spec index 15b0da5..221d3c6 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 5%{?dist} +Release: 6%{?dist} License: Python-2.0.1 @@ -337,6 +337,18 @@ Patch438: 00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.pat # CVE-2025-0938: Disallow square brackets ([ and ]) in domain names for parsed URLs Patch450: 00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch +# 00452 # eb11d070c5af7d1b5e47f4e02186152d08eaf793 +# Properly apply exported CFLAGS for dtrace/systemtap builds +# +# When using --with-dtrace the resulting object file could be missing +# specific CFLAGS exported by the build system due to the systemtap +# script using specific defaults. +# +# Exporting the CC and CFLAGS variables before the dtrace invocation +# allows us to properly apply CFLAGS exported by the build system +# even when cross-compiling. +Patch452: 00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1623,6 +1635,10 @@ CheckPython optimized # ====================================================== %changelog +* Mon Mar 31 2025 Charalampos Stratakis - 3.10.16-6 +- Properly apply exported CFLAGS for dtrace/systemtap builds +- Fixes: rhbz#2356303 + * Mon Feb 10 2025 Charalampos Stratakis - 3.10.16-5 - Security fix for CVE-2025-0938 - Fixes: rhbz#2343276 From a48837eb4a4e05c6ed544861e46c97abfd4f4a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 9 Apr 2025 09:54:55 +0200 Subject: [PATCH 31/53] Update to 3.10.17 Patches 438 and 450 merged upstream. --- ...treamtest-gh-119465-gh-119479-119484.patch | 66 ---------- ...-and-in-domain-names-for-parsed-urls.patch | 119 ------------------ python3.10.spec | 22 +--- sources | 4 +- 4 files changed, 7 insertions(+), 204 deletions(-) delete mode 100644 00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch delete mode 100644 00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch diff --git a/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch b/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch deleted file mode 100644 index 01a308d..0000000 --- a/00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Fri, 24 May 2024 01:23:55 +0200 -Subject: [PATCH] 00438: Fix ThreadedVSOCKSocketStreamTest (GH-119465) - (GH-119479) (#119484) - -Fix ThreadedVSOCKSocketStreamTest: if get_cid() returns the host -address or the "any" address, use the local communication address -(loopback): VMADDR_CID_LOCAL. - -On Linux 6.9, apparently, the /dev/vsock device is now available but -get_cid() returns VMADDR_CID_ANY (-1). - -(cherry picked from commit c750061047ee520d8299334df4b112fd983d7e48) - -Co-authored-by: Victor Stinner -(cherry picked from commit e94dbe4ed83460f18bd72563c5f09f6cdc71f604) - -Co-authored-by: Victor Stinner ---- - Lib/test/test_socket.py | 10 ++++++---- - 1 file changed, 6 insertions(+), 4 deletions(-) - -diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 0050298b41..12b90dcacb 100644 ---- a/Lib/test/test_socket.py -+++ b/Lib/test/test_socket.py -@@ -41,6 +41,7 @@ - # test unicode string and carriage return - MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') - -+VMADDR_CID_LOCAL = 1 - VSOCKPORT = 1234 - AIX = platform.system() == "AIX" - -@@ -124,8 +125,8 @@ def _have_socket_qipcrtr(): - - def _have_socket_vsock(): - """Check whether AF_VSOCK sockets are supported on this host.""" -- ret = get_cid() is not None -- return ret -+ cid = get_cid() -+ return (cid is not None) - - - def _have_socket_bluetooth(): -@@ -487,8 +488,6 @@ def clientTearDown(self): - @unittest.skipIf(fcntl is None, "need fcntl") - @unittest.skipUnless(HAVE_SOCKET_VSOCK, - 'VSOCK sockets required for this test.') --@unittest.skipUnless(get_cid() != 2, -- "This test can only be run on a virtual guest.") - class ThreadedVSOCKSocketStreamTest(unittest.TestCase, ThreadableTest): - - def __init__(self, methodName='runTest'): -@@ -509,6 +508,9 @@ def clientSetUp(self): - self.cli = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) - self.addCleanup(self.cli.close) - cid = get_cid() -+ if cid in (socket.VMADDR_CID_HOST, socket.VMADDR_CID_ANY): -+ # gh-119461: Use the local communication address (loopback) -+ cid = VMADDR_CID_LOCAL - self.cli.connect((cid, VSOCKPORT)) - - def testStream(self): diff --git a/00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch b/00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch deleted file mode 100644 index 5d005cb..0000000 --- a/00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch +++ /dev/null @@ -1,119 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Seth Michael Larson -Date: Fri, 31 Jan 2025 11:41:34 -0600 -Subject: [PATCH] 00450: CVE-2025-0938: Disallow square brackets ([ and ]) in - domain names for parsed URLs - -Co-authored-by: Peter Bierma ---- - Lib/test/test_urlparse.py | 37 ++++++++++++++++++- - Lib/urllib/parse.py | 20 +++++++++- - ...-01-28-14-08-03.gh-issue-105704.EnhHxu.rst | 4 ++ - 3 files changed, 58 insertions(+), 3 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst - -diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py -index f2ffc452e5..280644ef0b 100644 ---- a/Lib/test/test_urlparse.py -+++ b/Lib/test/test_urlparse.py -@@ -1149,16 +1149,51 @@ def test_invalid_bracketed_hosts(self): - self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query') - self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query') - self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a1') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a1') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:1a') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:1a') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:/') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:?') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@prefix.[v6a.ip]') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@[v6a.ip].suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip]') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip[') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip].suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip[suffix') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip') -+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[suffix') - - def test_splitting_bracketed_hosts(self): -- p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query') -+ p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]:1234/path?query') - self.assertEqual(p1.hostname, 'v6a.ip') - self.assertEqual(p1.username, 'user') - self.assertEqual(p1.path, '/path') -+ self.assertEqual(p1.port, 1234) - p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query') - self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test') - self.assertEqual(p2.username, 'user') - self.assertEqual(p2.path, '/path') -+ self.assertIs(p2.port, None) - p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query') - self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test') - self.assertEqual(p3.username, 'user') -diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py -index 07e3248504..e1ee36d98e 100644 ---- a/Lib/urllib/parse.py -+++ b/Lib/urllib/parse.py -@@ -442,6 +442,23 @@ def _checknetloc(netloc): - raise ValueError("netloc '" + netloc + "' contains invalid " + - "characters under NFKC normalization") - -+def _check_bracketed_netloc(netloc): -+ # Note that this function must mirror the splitting -+ # done in NetlocResultMixins._hostinfo(). -+ hostname_and_port = netloc.rpartition('@')[2] -+ before_bracket, have_open_br, bracketed = hostname_and_port.partition('[') -+ if have_open_br: -+ # No data is allowed before a bracket. -+ if before_bracket: -+ raise ValueError("Invalid IPv6 URL") -+ hostname, _, port = bracketed.partition(']') -+ # No data is allowed after the bracket but before the port delimiter. -+ if port and not port.startswith(":"): -+ raise ValueError("Invalid IPv6 URL") -+ else: -+ hostname, _, port = hostname_and_port.partition(':') -+ _check_bracketed_host(hostname) -+ - # Valid bracketed hosts are defined in - # https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/ - def _check_bracketed_host(hostname): -@@ -505,8 +522,7 @@ def urlsplit(url, scheme='', allow_fragments=True): - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") - if '[' in netloc and ']' in netloc: -- bracketed_host = netloc.partition('[')[2].partition(']')[0] -- _check_bracketed_host(bracketed_host) -+ _check_bracketed_netloc(netloc) - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: -diff --git a/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst -new file mode 100644 -index 0000000000..bff1bc6b0d ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst -@@ -0,0 +1,4 @@ -+When using :func:`urllib.parse.urlsplit` and :func:`urllib.parse.urlparse` host -+parsing would not reject domain names containing square brackets (``[`` and -+``]``). Square brackets are only valid for IPv6 and IPvFuture hosts according to -+`RFC 3986 Section 3.2.2 `__. diff --git a/python3.10.spec b/python3.10.spec index 221d3c6..d01fa94 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.16 +%global general_version %{pybasever}.17 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 6%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -322,21 +322,6 @@ Patch251: 00251-change-user-install-location.patch # https://github.com/GrahamDumpleton/mod_wsgi/issues/730 Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch -# 00438 # 640f507108d102da99fa2f39d268a43f86c97acb -# Fix ThreadedVSOCKSocketStreamTest (GH-119465) (GH-119479) (#119484) -# -# Fix ThreadedVSOCKSocketStreamTest: if get_cid() returns the host -# address or the "any" address, use the local communication address -# (loopback): VMADDR_CID_LOCAL. -# -# On Linux 6.9, apparently, the /dev/vsock device is now available but -# get_cid() returns VMADDR_CID_ANY (-1). -Patch438: 00438-fix-threadedvsocksocketstreamtest-gh-119465-gh-119479-119484.patch - -# 00450 # 4ab8663661748eb994c09e4ae89f59eb84c5d3ea -# CVE-2025-0938: Disallow square brackets ([ and ]) in domain names for parsed URLs -Patch450: 00450-cve-2025-0938-disallow-square-brackets-and-in-domain-names-for-parsed-urls.patch - # 00452 # eb11d070c5af7d1b5e47f4e02186152d08eaf793 # Properly apply exported CFLAGS for dtrace/systemtap builds # @@ -1635,6 +1620,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Apr 09 2025 Miro Hrončok - 3.10.17-1 +- Update to 3.10.17 + * Mon Mar 31 2025 Charalampos Stratakis - 3.10.16-6 - Properly apply exported CFLAGS for dtrace/systemtap builds - Fixes: rhbz#2356303 diff --git a/sources b/sources index fe5766d..7d7438b 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.16.tar.xz) = a6b004974db93f21590f00b9b1a569dfd8c3e334f8c6a8eb8997dec5646fa4515cfc02a7dabea9e4f75f908938dc64333ce4899b9a26aff97f9ffcb027fa2bca -SHA512 (Python-3.10.16.tar.xz.asc) = 18032fdb60a022d6381b1137789ada1c4bb198049616ed158fb548e728dd1fb95d116a86c41c852ca2acc9127baf3cdd9644a1e2df90ae9c3ffcd66eabf454d6 +SHA512 (Python-3.10.17.tar.xz) = 11b1e647f8db05041bcafe110698af8bf5e9f7c8d23c0e9eb568a60544bb9a9e9c3ac94b34f0898f0d4e6c23da3cb81f844ae9d9c8b750058763502eaba39880 +SHA512 (Python-3.10.17.tar.xz.asc) = f5fea78e2ea413c9befb6181787b3a5ea3bc0f5dbf808a94465b558c09353482cd34631a4c505f72aa343a2f4fae291453324cd8e6b18df214baaf4680fc17cf From 234d41ca6b6c9e9e805bb0de22a1c7d902daa65b Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Wed, 16 Apr 2025 22:47:54 +0200 Subject: [PATCH 32/53] test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler Fixes: rhbz#2355052 --- ...rver-on-oserror-in-connectionhandler.patch | 80 +++++++++++++++++++ python3.10.spec | 21 ++++- 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch diff --git a/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch b/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch new file mode 100644 index 0000000..7b83971 --- /dev/null +++ b/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch @@ -0,0 +1,80 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Mon, 11 Nov 2024 15:26:10 +0100 +Subject: [PATCH] 00458: test_ssl: Don't stop ThreadedEchoServer on OSError in + ConnectionHandler + +If `read()` in the ConnectionHandler thread raises `OSError` (except `ConnectionError`), +the ConnectionHandler shuts down the entire ThreadedEchoServer, +preventing further connections. +It also does that for `EPROTOTYPE` in `wrap_conn`. + +Make sure that the context manager *is* used, and remove the `server.stop()` +calls from ConnectionHandler. + +Backported from 3.12+: +https://github.com/python/cpython/pull/126503 + +Co-authored-by: Petr Viktorin +--- + Lib/test/test_ssl.py | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py +index a1a581a907..0f1397de73 100644 +--- a/Lib/test/test_ssl.py ++++ b/Lib/test/test_ssl.py +@@ -2488,7 +2488,6 @@ def wrap_conn(self): + # See also http://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ + if e.errno != errno.EPROTOTYPE and sys.platform != "darwin": + self.running = False +- self.server.stop() + self.close() + return False + else: +@@ -2623,10 +2622,6 @@ def run(self): + self.close() + self.running = False + +- # normally, we'd just stop here, but for the test +- # harness, we want to stop the server +- self.server.stop() +- + def __init__(self, certificate=None, ssl_version=None, + certreqs=None, cacerts=None, + chatty=True, connectionchatty=False, starttls_server=False, +@@ -2660,21 +2655,33 @@ def __init__(self, certificate=None, ssl_version=None, + self.conn_errors = [] + threading.Thread.__init__(self) + self.daemon = True ++ self._in_context = False + + def __enter__(self): ++ if self._in_context: ++ raise ValueError('Re-entering ThreadedEchoServer context') ++ self._in_context = True + self.start(threading.Event()) + self.flag.wait() + return self + + def __exit__(self, *args): ++ assert self._in_context ++ self._in_context = False + self.stop() + self.join() + + def start(self, flag=None): ++ if not self._in_context: ++ raise ValueError( ++ 'ThreadedEchoServer must be used as a context manager') + self.flag = flag + threading.Thread.start(self) + + def run(self): ++ if not self._in_context: ++ raise ValueError( ++ 'ThreadedEchoServer must be used as a context manager') + self.sock.settimeout(1.0) + self.sock.listen(5) + self.active = True diff --git a/python3.10.spec b/python3.10.spec index d01fa94..0512cb9 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -334,6 +334,21 @@ Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-g # even when cross-compiling. Patch452: 00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch +# 00458 # ee47b2530c18d1e0b414f5a0738ddce28e7510f4 +# test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler +# +# If `read()` in the ConnectionHandler thread raises `OSError` (except `ConnectionError`), +# the ConnectionHandler shuts down the entire ThreadedEchoServer, +# preventing further connections. +# It also does that for `EPROTOTYPE` in `wrap_conn`. +# +# Make sure that the context manager *is* used, and remove the `server.stop()` +# calls from ConnectionHandler. +# +# Backported from 3.12+: +# https://github.com/python/cpython/pull/126503 +Patch458: 00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1620,6 +1635,10 @@ CheckPython optimized # ====================================================== %changelog +* Wed Apr 16 2025 Charalampos Stratakis - 3.10.17-2 +- test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler +- Fixes: rhbz#2355052 + * Wed Apr 09 2025 Miro Hrončok - 3.10.17-1 - Update to 3.10.17 From 772c5976f4261a78d3bd7af3ab4331adccf5f9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Thu, 17 Apr 2025 11:43:22 +0200 Subject: [PATCH 33/53] Regenerate patches with updated importpatches script --- 00001-rpath.patch | 5 +++-- 00251-change-user-install-location.patch | 2 +- ...ing-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch | 4 ++-- ...y-apply-exported-cflags-for-dtrace-systemtap-builds.patch | 3 +-- ...-threadedechoserver-on-oserror-in-connectionhandler.patch | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/00001-rpath.patch b/00001-rpath.patch index 778c077..58e0d8f 100644 --- a/00001-rpath.patch +++ b/00001-rpath.patch @@ -1,8 +1,9 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 13 Jan 2010 21:25:18 +0000 -Subject: [PATCH] 00001: Fixup distutils/unixccompiler.py to remove standard - library path from rpath Was Patch0 in ivazquez' python3000 specfile +Subject: =?UTF-8?q?00001:=20Fixup=20distutils/unixccompiler.py=20to=20remo?= + =?UTF-8?q?ve=20standard=20library=20path=20from=20rpath=0AWas=20Patch0=20?= + =?UTF-8?q?in=20ivazquez'=20python3000=20specfile?= --- Lib/distutils/unixccompiler.py | 9 +++++++++ diff --git a/00251-change-user-install-location.patch b/00251-change-user-install-location.patch index b0a4c9c..7b68621 100644 --- a/00251-change-user-install-location.patch +++ b/00251-change-user-install-location.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 15 Feb 2021 12:19:27 +0100 -Subject: [PATCH] 00251: Change user install location +Subject: 00251: Change user install location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch b/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch index 7ab6c6c..7dbd3bb 100644 --- a/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch +++ b/00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch @@ -1,8 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Tue, 7 Dec 2021 14:41:59 +0100 -Subject: [PATCH] 00371: Revert "bpo-1596321: Fix threading._shutdown() for the - main thread (GH-28549) (GH-28589)" +Subject: 00371: Revert "bpo-1596321: Fix threading._shutdown() for the main + thread (GH-28549) (GH-28589)" This reverts commit 38c67738c64304928c68d5c2bd78bbb01d979b94. It introduced regression causing FreeIPA's tests to fail. diff --git a/00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch b/00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch index 359d9ac..2e2f3c6 100644 --- a/00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch +++ b/00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch @@ -2,8 +2,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:29:04 +0200 -Subject: [PATCH] 00452: Properly apply exported CFLAGS for dtrace/systemtap - builds +Subject: 00452: Properly apply exported CFLAGS for dtrace/systemtap builds When using --with-dtrace the resulting object file could be missing specific CFLAGS exported by the build system due to the systemtap diff --git a/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch b/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch index 7b83971..ab62987 100644 --- a/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch +++ b/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch @@ -2,7 +2,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:26:10 +0100 -Subject: [PATCH] 00458: test_ssl: Don't stop ThreadedEchoServer on OSError in +Subject: 00458: test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler If `read()` in the ConnectionHandler thread raises `OSError` (except `ConnectionError`), From 75538d3684a89581b6b85a5336f0cd6a4f19619e Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Tue, 13 May 2025 02:32:52 +0200 Subject: [PATCH 34/53] Fix PySSL_SetError handling SSL_ERROR_SYSCALL This fixes random flakiness of test_ssl on stressed machines --- 00001-rpath.patch | 5 +- ..._seterror-handling-ssl_error_syscall.patch | 214 ++++++++++++++++++ python3.10.spec | 22 +- 3 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch diff --git a/00001-rpath.patch b/00001-rpath.patch index 58e0d8f..9498516 100644 --- a/00001-rpath.patch +++ b/00001-rpath.patch @@ -1,9 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 13 Jan 2010 21:25:18 +0000 -Subject: =?UTF-8?q?00001:=20Fixup=20distutils/unixccompiler.py=20to=20remo?= - =?UTF-8?q?ve=20standard=20library=20path=20from=20rpath=0AWas=20Patch0=20?= - =?UTF-8?q?in=20ivazquez'=20python3000=20specfile?= +Subject: 00001: Fixup distutils/unixccompiler.py to remove standard library + path from rpath Was Patch0 in ivazquez' python3000 specfile --- Lib/distutils/unixccompiler.py | 9 +++++++++ diff --git a/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch b/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch new file mode 100644 index 0000000..5c0cda1 --- /dev/null +++ b/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch @@ -0,0 +1,214 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: yevgeny hong +Date: Tue, 26 Mar 2024 16:45:43 +0900 +Subject: 00462: Fix PySSL_SetError handling SSL_ERROR_SYSCALL + +Python 3.10 changed from using SSL_write() and SSL_read() to SSL_write_ex() and +SSL_read_ex(), but did not update handling of the return value. + +Change error handling so that the return value is not examined. +OSError (not EOF) is now returned when retval is 0. + +This resolves the issue of failing tests when a system is +stressed on OpenSSL 3.5. + +Co-authored-by: Serhiy Storchaka +Co-authored-by: Petr Viktorin +--- + Lib/test/test_ssl.py | 41 +++++++--------- + ...-02-18-09-50-31.gh-issue-115627.HGchj0.rst | 2 + + Modules/_ssl.c | 48 +++++++------------ + 3 files changed, 38 insertions(+), 53 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst + +diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py +index 0f1397de73..21d3676c22 100644 +--- a/Lib/test/test_ssl.py ++++ b/Lib/test/test_ssl.py +@@ -2605,16 +2605,18 @@ def run(self): + self.write(msg.lower()) + except OSError as e: + # handles SSLError and socket errors ++ if isinstance(e, ConnectionError): ++ # OpenSSL 1.1.1 sometimes raises ++ # ConnectionResetError when connection is not ++ # shut down gracefully. ++ if self.server.chatty and support.verbose: ++ print(f" Connection reset by peer: {self.addr}") ++ ++ self.close() ++ self.running = False ++ return + if self.server.chatty and support.verbose: +- if isinstance(e, ConnectionError): +- # OpenSSL 1.1.1 sometimes raises +- # ConnectionResetError when connection is not +- # shut down gracefully. +- print( +- f" Connection reset by peer: {self.addr}" +- ) +- else: +- handle_error("Test server failure:\n") ++ handle_error("Test server failure:\n") + try: + self.write(b"ERROR\n") + except OSError: +@@ -3296,23 +3298,16 @@ def test_wrong_cert_tls13(self): + client_context.wrap_socket(socket.socket(), + server_hostname=hostname, + suppress_ragged_eofs=False) as s: +- # TLS 1.3 perform client cert exchange after handshake + s.connect((HOST, server.port)) +- try: ++ with self.assertRaisesRegex( ++ OSError, ++ 'alert unknown ca|EOF occurred|TLSV1_ALERT_UNKNOWN_CA|closed by the remote host|Connection reset by peer' ++ ): ++ # TLS 1.3 perform client cert exchange after handshake + s.write(b'data') + s.read(1000) + s.write(b'should have failed already') + s.read(1000) +- except ssl.SSLError as e: +- if support.verbose: +- sys.stdout.write("\nSSLError is %r\n" % e) +- except OSError as e: +- if e.errno != errno.ECONNRESET: +- raise +- if support.verbose: +- sys.stdout.write("\nsocket.error is %r\n" % e) +- else: +- self.fail("Use of invalid cert should have failed!") + + def test_rude_shutdown(self): + """A brutal shutdown of an SSL server should raise an OSError +@@ -4558,8 +4553,8 @@ def msg_cb(conn, direction, version, content_type, msg_type, data): + # test sometimes fails with EOF error. Test passes as long as + # server aborts connection with an error. + with self.assertRaisesRegex( +- ssl.SSLError, +- '(certificate required|EOF occurred)' ++ OSError, ++ 'certificate required|EOF occurred|closed by the remote host|Connection reset by peer' + ): + # receive CertificateRequest + data = s.recv(1024) +diff --git a/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst b/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst +new file mode 100644 +index 0000000000..75d926ab59 +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst +@@ -0,0 +1,2 @@ ++Fix the :mod:`ssl` module error handling of connection terminate by peer. ++It now throws an OSError with the appropriate error code instead of an EOFError. +diff --git a/Modules/_ssl.c b/Modules/_ssl.c +index 6852cc363b..91bf27bfef 100644 +--- a/Modules/_ssl.c ++++ b/Modules/_ssl.c +@@ -582,7 +582,7 @@ PySSL_ChainExceptions(PySSLSocket *sslsock) { + } + + static PyObject * +-PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno) ++PySSL_SetError(PySSLSocket *sslsock, const char *filename, int lineno) + { + PyObject *type; + char *errstr = NULL; +@@ -595,7 +595,6 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno) + _sslmodulestate *state = get_state_sock(sslsock); + type = state->PySSLErrorObject; + +- assert(ret <= 0); + e = ERR_peek_last_error(); + + if (sslsock->ssl != NULL) { +@@ -628,32 +627,21 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno) + case SSL_ERROR_SYSCALL: + { + if (e == 0) { +- PySocketSockObject *s = GET_SOCKET(sslsock); +- if (ret == 0 || (((PyObject *)s) == Py_None)) { ++ /* underlying BIO reported an I/O error */ ++ ERR_clear_error(); ++#ifdef MS_WINDOWS ++ if (err.ws) { ++ return PyErr_SetFromWindowsErr(err.ws); ++ } ++#endif ++ if (err.c) { ++ errno = err.c; ++ return PyErr_SetFromErrno(PyExc_OSError); ++ } ++ else { + p = PY_SSL_ERROR_EOF; + type = state->PySSLEOFErrorObject; + errstr = "EOF occurred in violation of protocol"; +- } else if (s && ret == -1) { +- /* underlying BIO reported an I/O error */ +- ERR_clear_error(); +-#ifdef MS_WINDOWS +- if (err.ws) { +- return PyErr_SetFromWindowsErr(err.ws); +- } +-#endif +- if (err.c) { +- errno = err.c; +- return PyErr_SetFromErrno(PyExc_OSError); +- } +- else { +- p = PY_SSL_ERROR_EOF; +- type = state->PySSLEOFErrorObject; +- errstr = "EOF occurred in violation of protocol"; +- } +- } else { /* possible? */ +- p = PY_SSL_ERROR_SYSCALL; +- type = state->PySSLSyscallErrorObject; +- errstr = "Some I/O error occurred"; + } + } else { + if (ERR_GET_LIB(e) == ERR_LIB_SYS) { +@@ -1014,7 +1002,7 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self) + err.ssl == SSL_ERROR_WANT_WRITE); + Py_XDECREF(sock); + if (ret < 1) +- return PySSL_SetError(self, ret, __FILE__, __LINE__); ++ return PySSL_SetError(self, __FILE__, __LINE__); + if (PySSL_ChainExceptions(self) < 0) + return NULL; + Py_RETURN_NONE; +@@ -2433,7 +2421,7 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) + + Py_XDECREF(sock); + if (retval == 0) +- return PySSL_SetError(self, retval, __FILE__, __LINE__); ++ return PySSL_SetError(self, __FILE__, __LINE__); + if (PySSL_ChainExceptions(self) < 0) + return NULL; + return PyLong_FromSize_t(count); +@@ -2463,7 +2451,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self) + self->err = err; + + if (count < 0) +- return PySSL_SetError(self, count, __FILE__, __LINE__); ++ return PySSL_SetError(self, __FILE__, __LINE__); + else + return PyLong_FromLong(count); + } +@@ -2585,7 +2573,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, + err.ssl == SSL_ERROR_WANT_WRITE); + + if (retval == 0) { +- PySSL_SetError(self, retval, __FILE__, __LINE__); ++ PySSL_SetError(self, __FILE__, __LINE__); + goto error; + } + if (self->exc_type != NULL) +@@ -2709,7 +2697,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) + } + if (ret < 0) { + Py_XDECREF(sock); +- PySSL_SetError(self, ret, __FILE__, __LINE__); ++ PySSL_SetError(self, __FILE__, __LINE__); + return NULL; + } + if (self->exc_type != NULL) diff --git a/python3.10.spec b/python3.10.spec index 0512cb9..b91f16c 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -286,8 +286,7 @@ Source11: idle3.appdata.xml # (Patches taken from github.com/fedora-python/cpython) # 00001 # d06a8853cf4bae9e115f45e1d531d2dc152c5cc8 -# Fixup distutils/unixccompiler.py to remove standard library path from rpath -# Was Patch0 in ivazquez' python3000 specfile +# Fixup distutils/unixccompiler.py to remove standard library path from rpath Was Patch0 in ivazquez' python3000 specfile Patch1: 00001-rpath.patch # 00251 # 8448a0d6edd44d0818b8c02dc603b769b5363e5b @@ -349,6 +348,19 @@ Patch452: 00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch # https://github.com/python/cpython/pull/126503 Patch458: 00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch +# 00462 # f0db87ee65704fa5545ea25f2cca8c43fc639fab +# Fix PySSL_SetError handling SSL_ERROR_SYSCALL +# +# Python 3.10 changed from using SSL_write() and SSL_read() to SSL_write_ex() and +# SSL_read_ex(), but did not update handling of the return value. +# +# Change error handling so that the return value is not examined. +# OSError (not EOF) is now returned when retval is 0. +# +# This resolves the issue of failing tests when a system is +# stressed on OpenSSL 3.5. +Patch462: 00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1635,6 +1647,10 @@ CheckPython optimized # ====================================================== %changelog +* Tue May 13 2025 Charalampos Stratakis - 3.10.17-3 +- Fix PySSL_SetError handling SSL_ERROR_SYSCALL +- This fixes random flakiness of test_ssl on stressed machines + * Wed Apr 16 2025 Charalampos Stratakis - 3.10.17-2 - test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler - Fixes: rhbz#2355052 From 6c238008d198b1eba6f88c3fcec17d99b94e1f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Wed, 4 Jun 2025 09:39:00 +0200 Subject: [PATCH 35/53] Update to 3.10.18 --- ...rver-on-oserror-in-connectionhandler.patch | 80 ------------------- python3.10.spec | 22 ++--- sources | 4 +- 3 files changed, 7 insertions(+), 99 deletions(-) delete mode 100644 00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch diff --git a/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch b/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch deleted file mode 100644 index ab62987..0000000 --- a/00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Mon, 11 Nov 2024 15:26:10 +0100 -Subject: 00458: test_ssl: Don't stop ThreadedEchoServer on OSError in - ConnectionHandler - -If `read()` in the ConnectionHandler thread raises `OSError` (except `ConnectionError`), -the ConnectionHandler shuts down the entire ThreadedEchoServer, -preventing further connections. -It also does that for `EPROTOTYPE` in `wrap_conn`. - -Make sure that the context manager *is* used, and remove the `server.stop()` -calls from ConnectionHandler. - -Backported from 3.12+: -https://github.com/python/cpython/pull/126503 - -Co-authored-by: Petr Viktorin ---- - Lib/test/test_ssl.py | 17 ++++++++++++----- - 1 file changed, 12 insertions(+), 5 deletions(-) - -diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py -index a1a581a907..0f1397de73 100644 ---- a/Lib/test/test_ssl.py -+++ b/Lib/test/test_ssl.py -@@ -2488,7 +2488,6 @@ def wrap_conn(self): - # See also http://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ - if e.errno != errno.EPROTOTYPE and sys.platform != "darwin": - self.running = False -- self.server.stop() - self.close() - return False - else: -@@ -2623,10 +2622,6 @@ def run(self): - self.close() - self.running = False - -- # normally, we'd just stop here, but for the test -- # harness, we want to stop the server -- self.server.stop() -- - def __init__(self, certificate=None, ssl_version=None, - certreqs=None, cacerts=None, - chatty=True, connectionchatty=False, starttls_server=False, -@@ -2660,21 +2655,33 @@ def __init__(self, certificate=None, ssl_version=None, - self.conn_errors = [] - threading.Thread.__init__(self) - self.daemon = True -+ self._in_context = False - - def __enter__(self): -+ if self._in_context: -+ raise ValueError('Re-entering ThreadedEchoServer context') -+ self._in_context = True - self.start(threading.Event()) - self.flag.wait() - return self - - def __exit__(self, *args): -+ assert self._in_context -+ self._in_context = False - self.stop() - self.join() - - def start(self, flag=None): -+ if not self._in_context: -+ raise ValueError( -+ 'ThreadedEchoServer must be used as a context manager') - self.flag = flag - threading.Thread.start(self) - - def run(self): -+ if not self._in_context: -+ raise ValueError( -+ 'ThreadedEchoServer must be used as a context manager') - self.sock.settimeout(1.0) - self.sock.listen(5) - self.active = True diff --git a/python3.10.spec b/python3.10.spec index b91f16c..b722825 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.17 +%global general_version %{pybasever}.18 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -333,21 +333,6 @@ Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-g # even when cross-compiling. Patch452: 00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch -# 00458 # ee47b2530c18d1e0b414f5a0738ddce28e7510f4 -# test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler -# -# If `read()` in the ConnectionHandler thread raises `OSError` (except `ConnectionError`), -# the ConnectionHandler shuts down the entire ThreadedEchoServer, -# preventing further connections. -# It also does that for `EPROTOTYPE` in `wrap_conn`. -# -# Make sure that the context manager *is* used, and remove the `server.stop()` -# calls from ConnectionHandler. -# -# Backported from 3.12+: -# https://github.com/python/cpython/pull/126503 -Patch458: 00458-test_ssl-don-t-stop-threadedechoserver-on-oserror-in-connectionhandler.patch - # 00462 # f0db87ee65704fa5545ea25f2cca8c43fc639fab # Fix PySSL_SetError handling SSL_ERROR_SYSCALL # @@ -1647,6 +1632,9 @@ CheckPython optimized # ====================================================== %changelog +* Wed Jun 04 2025 Tomáš Hrnčiar - 3.10.18-1 +- Update to 3.10.18 + * Tue May 13 2025 Charalampos Stratakis - 3.10.17-3 - Fix PySSL_SetError handling SSL_ERROR_SYSCALL - This fixes random flakiness of test_ssl on stressed machines diff --git a/sources b/sources index 7d7438b..64bd7d3 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.17.tar.xz) = 11b1e647f8db05041bcafe110698af8bf5e9f7c8d23c0e9eb568a60544bb9a9e9c3ac94b34f0898f0d4e6c23da3cb81f844ae9d9c8b750058763502eaba39880 -SHA512 (Python-3.10.17.tar.xz.asc) = f5fea78e2ea413c9befb6181787b3a5ea3bc0f5dbf808a94465b558c09353482cd34631a4c505f72aa343a2f4fae291453324cd8e6b18df214baaf4680fc17cf +SHA512 (Python-3.10.18.tar.xz) = 2c444f024cd15babd156964b50aa0245020a17c0c600250ffcf112e91594a3d6e1fb87c9b87a57cb7a802dae90004d801ed95a3103d79aefb8ed714279997708 +SHA512 (Python-3.10.18.tar.xz.asc) = dbe1426b1b8371bf390b53595d2c656fbd311fed55ad3c705e175fa0c72d51a022f286877ec55ad74547a3b57ae945c9af7ece4dacc7132e21b9b7eb4421add6 From ec3cbfebe9b9284a1b03109477aa73457b07447d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 26 Jun 2025 13:13:07 +0200 Subject: [PATCH 36/53] Fixup the patch 1 commit message after the importpatches/exportpatches change This fixes up 772c5976f4261a78d3bd7af3ab4331adccf5f9fe + 75538d3684a89581b6b85a5336f0cd6a4f19619e. --- 00001-rpath.patch | 3 ++- python3.10.spec | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/00001-rpath.patch b/00001-rpath.patch index 9498516..cd063e5 100644 --- a/00001-rpath.patch +++ b/00001-rpath.patch @@ -2,8 +2,9 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 13 Jan 2010 21:25:18 +0000 Subject: 00001: Fixup distutils/unixccompiler.py to remove standard library - path from rpath Was Patch0 in ivazquez' python3000 specfile + path from rpath +Was Patch0 in ivazquez' python3000 specfile --- Lib/distutils/unixccompiler.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/python3.10.spec b/python3.10.spec index b722825..64ead49 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -286,7 +286,9 @@ Source11: idle3.appdata.xml # (Patches taken from github.com/fedora-python/cpython) # 00001 # d06a8853cf4bae9e115f45e1d531d2dc152c5cc8 -# Fixup distutils/unixccompiler.py to remove standard library path from rpath Was Patch0 in ivazquez' python3000 specfile +# Fixup distutils/unixccompiler.py to remove standard library path from rpath +# +# Was Patch0 in ivazquez' python3000 specfile Patch1: 00001-rpath.patch # 00251 # 8448a0d6edd44d0818b8c02dc603b769b5363e5b From 9d6cf58680bd1e6b1724e619140915f784e59437 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 25 Jul 2025 11:18:56 +0000 Subject: [PATCH 37/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 64ead49..9e6fd43 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -1634,6 +1634,9 @@ CheckPython optimized # ====================================================== %changelog +* Fri Jul 25 2025 Fedora Release Engineering - 3.10.18-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + * Wed Jun 04 2025 Tomáš Hrnčiar - 3.10.18-1 - Update to 3.10.18 From 207259522d0b3054a2128fad5695067bda8bd0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Zachar?= Date: Fri, 25 Jul 2025 13:33:03 +0200 Subject: [PATCH 38/53] Drop STI and use tmt instead Resolves: rhbz#2383056 --- {tests/.fmf => .fmf}/version | 0 plan.fmf | 61 ++++++++++++++++++++++++++++++++++++ tests/provision.fmf | 4 --- tests/tests.yml | 55 -------------------------------- 4 files changed, 61 insertions(+), 59 deletions(-) rename {tests/.fmf => .fmf}/version (100%) create mode 100644 plan.fmf delete mode 100644 tests/provision.fmf delete mode 100644 tests/tests.yml diff --git a/tests/.fmf/version b/.fmf/version similarity index 100% rename from tests/.fmf/version rename to .fmf/version diff --git a/plan.fmf b/plan.fmf new file mode 100644 index 0000000..71f3e63 --- /dev/null +++ b/plan.fmf @@ -0,0 +1,61 @@ +execute: + how: tmt + +provision: + hardware: + memory: '>= 3 GB' + +environment: + pybasever: '3.10' + +discover: + - name: tests_python + how: shell + url: https://src.fedoraproject.org/tests/python.git + tests: + - name: smoke + path: /smoke + test: "VERSION=${pybasever} ./venv.sh" + - name: smoke_virtualenv + path: /smoke + test: "VERSION=${pybasever} METHOD=virtualenv ./venv.sh" + - name: debugsmoke + path: /smoke + test: "PYTHON=python${pybasever}d TOX=false VERSION=${pybasever} ./venv.sh" + - name: selftest + path: /selftest + test: "VERSION=${pybasever} X='' ./parallel.sh" + - name: debugtest + path: /selftest + test: "VERSION=${pybasever} PYTHON=python${pybasever}d X='' ./parallel.sh" + - name: debugflags + path: /flags + test: "python${pybasever}d ./assertflags.py -O0" + - name: marshalparser + path: /marshalparser + test: "VERSION=${pybasever} SAMPLE=10 ./test_marshalparser_compatibility.sh" + +prepare: + - name: Install dependencies + how: install + package: + - gcc # for extension building in venv and selftest + - gdb # for test_gdb + - "python${pybasever}" # the test subject + - "python${pybasever}-debug" # for leak testing + - "python${pybasever}-devel" # for extension building in venv and selftest + - "python${pybasever}-tkinter" # for selftest + - "python${pybasever}-test" # for selftest + - tox # for venv tests + - virtualenv # for virtualenv tests + - glibc-all-langpacks # for locale tests + - marshalparser # for testing compatibility (magic numbers) with marshalparser + - rpm # for debugging + - dnf # for upgrade + - name: Update packages + how: shell + script: dnf upgrade -y + - name: rpm_qa + order: 100 + how: shell + script: rpm -qa | sort | tee $TMT_PLAN_DATA/rpmqa.txt diff --git a/tests/provision.fmf b/tests/provision.fmf deleted file mode 100644 index 1a4f0f0..0000000 --- a/tests/provision.fmf +++ /dev/null @@ -1,4 +0,0 @@ ---- -standard-inventory-qcow2: - qemu: - m: 3G # Amount of VM memory diff --git a/tests/tests.yml b/tests/tests.yml deleted file mode 100644 index 460b4a1..0000000 --- a/tests/tests.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -- hosts: localhost - tags: - - classic - tasks: - - dnf: - name: "*" - state: latest - -- hosts: localhost - roles: - - role: standard-test-basic - tags: - - classic - repositories: - - repo: "https://src.fedoraproject.org/tests/python.git" - dest: "python" - pybasever: "3.10" - tests: - - rpm_qa: - run: rpm -qa - - smoke: - dir: python/smoke - run: "VERSION={{ pybasever }} ./venv.sh" - - smoke_virtualenv: - dir: python/smoke - run: "VERSION={{ pybasever }} METHOD=virtualenv ./venv.sh" - - debugsmoke: - dir: python/smoke - run: "PYTHON=python{{ pybasever }}d TOX=false VERSION={{ pybasever }} ./venv.sh" - - selftest: - dir: python/selftest - run: "VERSION={{ pybasever }} X='' ./parallel.sh" - - debugtest: - dir: python/selftest - run: "VERSION={{ pybasever }} PYTHON=python{{ pybasever }}d X='' ./parallel.sh" - - debugflags: - dir: python/flags - run: "python{{ pybasever }}d ./assertflags.py -O0" - - marshalparser: - dir: python/marshalparser - run: "VERSION={{ pybasever }} SAMPLE=10 test_marshalparser_compatibility.sh" - required_packages: - - gcc # for extension building in venv and selftest - - gdb # for test_gdb - - "python{{ pybasever }}" # the test subject - - "python{{ pybasever }}-debug" # for leak testing - - "python{{ pybasever }}-devel" # for extension building in venv and selftest - - "python{{ pybasever }}-tkinter" # for selftest - - "python{{ pybasever }}-test" # for selftest - - tox # for venv tests - - virtualenv # for virtualenv tests - - glibc-all-langpacks # for locale tests - - marshalparser # for testing compatibility (magic numbers) with marshalparser - - rpm # for debugging From b1fc74dd76704c00bde4673ae6520484d77e1dca Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Tue, 26 Aug 2025 10:01:22 +0200 Subject: [PATCH 39/53] rpminspect: Wrap strings with globs with single quotes It seems to be required since rpminspect 2.0: https://github.com/rpminspect/rpminspect/commit/1593c38ce46980e0279150706d1f7c7bdd82de28 --- rpminspect.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rpminspect.yaml b/rpminspect.yaml index 83dfb5e..8cc18cb 100644 --- a/rpminspect.yaml +++ b/rpminspect.yaml @@ -1,22 +1,22 @@ # exclude test XML data (not always valid) from XML validity check: xml: ignore: - - /usr/lib*/python*/test/xmltestdata/* - - /usr/lib*/python*/test/xmltestdata/*/* + - '/usr/lib*/python*/test/xmltestdata/*' + - '/usr/lib*/python*/test/xmltestdata/*/*' # exclude _socket from ipv4 only functions check, it has both ipv4 and ipv6 only badfuncs: allowed: - /usr/lib*/python*/lib-dynload/_socket.*: + '/usr/lib*/python*/lib-dynload/_socket.*': - inet_aton - inet_ntoa # exclude the debug build from annocheck entirely annocheck: ignore: - - /usr/bin/python*d - - /usr/lib*/libpython*d.so.1.0 - - /usr/lib*/python*/lib-dynload/*.cpython-*d-*-*-*.so + - '/usr/bin/python*d' + - '/usr/lib*/libpython*d.so.1.0' + - '/usr/lib*/python*/lib-dynload/*.cpython-*d-*-*-*.so' # don't report changed content of compiled files # that is expected with every toolchain update and not reproducible yet From a3bdce551cae631446ca1d887cf1c58cbaf31b45 Mon Sep 17 00:00:00 2001 From: Karolina Surma Date: Fri, 10 Oct 2025 14:22:08 +0200 Subject: [PATCH 40/53] Update to Python 3.10.19 --- python3.10.spec | 9 ++++++--- sources | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index 9e6fd43..ed7b1b6 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.18 +%global general_version %{pybasever}.19 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -68,7 +68,7 @@ License: Python-2.0.1 # from Python with the versions below. # This needs to be manually updated when we update Python. %global pip_version 23.0.1 -%global setuptools_version 65.5.0 +%global setuptools_version 79.0.1 # Expensive optimizations (mainly, profile-guided optimizations) %bcond_without optimizations @@ -1634,6 +1634,9 @@ CheckPython optimized # ====================================================== %changelog +* Fri Oct 10 2025 Karolina Surma - 3.10.19-1 +- Update to Python 3.10.19 + * Fri Jul 25 2025 Fedora Release Engineering - 3.10.18-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild diff --git a/sources b/sources index 64bd7d3..6de6cda 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.18.tar.xz) = 2c444f024cd15babd156964b50aa0245020a17c0c600250ffcf112e91594a3d6e1fb87c9b87a57cb7a802dae90004d801ed95a3103d79aefb8ed714279997708 -SHA512 (Python-3.10.18.tar.xz.asc) = dbe1426b1b8371bf390b53595d2c656fbd311fed55ad3c705e175fa0c72d51a022f286877ec55ad74547a3b57ae945c9af7ece4dacc7132e21b9b7eb4421add6 +SHA512 (Python-3.10.19.tar.xz) = 9ea1e952bb5f7c9cc8d3efd8e7ae0059c129b6e09d12ded7d4e22476fad35f2c8186b1e003ea35a0099ffa5ff0a114985c3e960fba84e7037670b840a3e19c68 +SHA512 (Python-3.10.19.tar.xz.asc) = 64bcee68c2f4e0718daaf19d405f4a5bd0ce3fc3d13bb5cbae8d4a36e419dd104c94323b497f1d06110f6b62a1b3b351279c534145f4e8cea9af0d7bb0a8b5c9 From bd23e23416c9d72129581321bfdc90b279c93f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Fri, 5 Sep 2025 11:41:55 +0000 Subject: [PATCH 41/53] Inject SBOM into the installed wheels (when using the bundled ones) --- python3.10.spec | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python3.10.spec b/python3.10.spec index ed7b1b6..2c5aee0 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -246,6 +246,9 @@ BuildRequires: /usr/sbin/ifconfig %if %{with rpmwheels} BuildRequires: %{python_wheel_pkg_prefix}-setuptools-wheel BuildRequires: %{python_wheel_pkg_prefix}-pip-wheel +%else +# For %%python_wheel_inject_sbom +BuildRequires: python-rpm-macros %endif %if %{without bootstrap} @@ -1064,6 +1067,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 # ====================================================== From 14cfcd8b1f83f11d889d8531d462f43a987d5f50 Mon Sep 17 00:00:00 2001 From: Karolina Surma Date: Tue, 6 Jan 2026 17:53:12 +0100 Subject: [PATCH 42/53] Require at least the same expat version as used during the build The versioned requirement is no longer valid - this happens again now with expat 2.7.2 introducing new symbols. Make the versioned requirement future-proof - the generated version will always match at least the one present in the buildroot during the Python build. --- python3.10.spec | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index 2c5aee0..6a4fa7e 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -192,8 +192,7 @@ BuildRequires: bluez-libs-devel BuildRequires: bzip2 BuildRequires: bzip2-devel BuildRequires: desktop-file-utils -# See the runtime requirement in the -libs subpackage -BuildRequires: expat-devel >= 2.6 +BuildRequires: expat-devel BuildRequires: findutils BuildRequires: gcc-c++ @@ -497,12 +496,20 @@ Recommends: (%{pkgname}-tkinter%{?_isa} = %{version}-%{release} if tk%{?_isa}) Requires: tzdata # The requirement on libexpat is generated, but we need to version it. -# When built with expat >= 2.6, but installed with older expat, we get: +# 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_SetReparseDeferralEnabled +# 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. # Other subpackages (like -debug) also need this, but they all depend on -libs. -Requires: expat >= 2.6 +%global expat_version %(LANG=C rpm -q --qf '%%{version}' expat.%{_target_cpu} | sed 's/.*not installed/0/') +Requires: expat >= %{expat_version} + # Since patch 251 changed from distutils to sysconfig, pip needed to be adapted # The previous versions could cause serious bugs during `sudo pip install --upgrade ...` @@ -1642,6 +1649,9 @@ CheckPython optimized # ====================================================== %changelog +* Tue Jan 06 2026 Karolina Surma - 3.10.19-2 +- Require at least the same expat version as used during the build time + * Fri Oct 10 2025 Karolina Surma - 3.10.19-1 - Update to Python 3.10.19 From 311900d817ce3e8b6439334b7140e827753ca833 Mon Sep 17 00:00:00 2001 From: Karolina Surma Date: Mon, 12 Jan 2026 11:55:39 +0100 Subject: [PATCH 43/53] Extend the expat requirement to differentiate between 32 and 64 arches (cherry picked from python3.15 commit a5ca170d3f1e6e19f5df66f21482c2b10593af42) --- python3.10.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 6a4fa7e..2691ab5 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -508,7 +508,7 @@ Requires: tzdata # was used during the build time. # Other subpackages (like -debug) also need this, but they all depend on -libs. %global expat_version %(LANG=C rpm -q --qf '%%{version}' expat.%{_target_cpu} | sed 's/.*not installed/0/') -Requires: expat >= %{expat_version} +Requires: expat%{?_isa} >= %{expat_version} # Since patch 251 changed from distutils to sysconfig, pip needed to be adapted From 6c0fab4bc9126543051bdacd26cd75d3d8918b2d Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 17 Jan 2026 10:39:54 +0000 Subject: [PATCH 44/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 2691ab5..de79e10 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -1649,6 +1649,9 @@ CheckPython optimized # ====================================================== %changelog +* Sat Jan 17 2026 Fedora Release Engineering - 3.10.19-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild + * Tue Jan 06 2026 Karolina Surma - 3.10.19-2 - Require at least the same expat version as used during the build time From e0dac6a2c5af911856a043bb5280c77a2494b3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Mon, 9 Feb 2026 10:38:43 +0100 Subject: [PATCH 45/53] Security fixes for CVE-2026-0865, CVE-2025-15366 and CVE-2025-15367 --- 00473-cve-2026-0865.patch | 90 ++++++++++++++++++++++++++++++++++++++ 00474-cve-2025-15366.patch | 61 ++++++++++++++++++++++++++ 00475-cve-2025-15367.patch | 61 ++++++++++++++++++++++++++ python3.10.spec | 30 ++++++++++++- 4 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 00473-cve-2026-0865.patch create mode 100644 00474-cve-2025-15366.patch create mode 100644 00475-cve-2025-15367.patch diff --git a/00473-cve-2026-0865.patch b/00473-cve-2026-0865.patch new file mode 100644 index 0000000..3504e6b --- /dev/null +++ b/00473-cve-2026-0865.patch @@ -0,0 +1,90 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Seth Michael Larson +Date: Sat, 17 Jan 2026 11:46:21 -0600 +Subject: 00473: CVE-2026-0865 + + gh-143916: Reject control characters in wsgiref.headers.Headers (GH-143917) + +* Add 'test.support' fixture for C0 control characters +* gh-143916: Reject control characters in wsgiref.headers.Headers +--- + Lib/test/support/__init__.py | 7 +++++++ + Lib/test/test_wsgiref.py | 12 +++++++++++- + Lib/wsgiref/headers.py | 3 +++ + .../2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst | 2 ++ + 4 files changed, 23 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst + +diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py +index 0d3b9634f1..d0492fe191 100644 +--- a/Lib/test/support/__init__.py ++++ b/Lib/test/support/__init__.py +@@ -2157,3 +2157,10 @@ def adjust_int_max_str_digits(max_digits): + yield + finally: + sys.set_int_max_str_digits(current) ++ ++ ++def control_characters_c0() -> list[str]: ++ """Returns a list of C0 control characters as strings. ++ C0 control characters defined as the byte range 0x00-0x1F, and 0x7F. ++ """ ++ return [chr(c) for c in range(0x00, 0x20)] + ["\x7F"] +diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py +index 42094f4677..01ca51ba45 100644 +--- a/Lib/test/test_wsgiref.py ++++ b/Lib/test/test_wsgiref.py +@@ -1,6 +1,6 @@ + from unittest import mock + from test import support +-from test.support import socket_helper ++from test.support import socket_helper, control_characters_c0 + from test.support import warnings_helper + from test.test_httpservers import NoLogRequestHandler + from unittest import TestCase +@@ -527,6 +527,16 @@ def testExtras(self): + '\r\n' + ) + ++ def testRaisesControlCharacters(self): ++ headers = Headers() ++ for c0 in control_characters_c0(): ++ self.assertRaises(ValueError, headers.__setitem__, f"key{c0}", "val") ++ self.assertRaises(ValueError, headers.__setitem__, "key", f"val{c0}") ++ self.assertRaises(ValueError, headers.add_header, f"key{c0}", "val", param="param") ++ self.assertRaises(ValueError, headers.add_header, "key", f"val{c0}", param="param") ++ self.assertRaises(ValueError, headers.add_header, "key", "val", param=f"param{c0}") ++ ++ + class ErrorHandler(BaseCGIHandler): + """Simple handler subclass for testing BaseHandler""" + +diff --git a/Lib/wsgiref/headers.py b/Lib/wsgiref/headers.py +index fab851c5a4..fd98e85d75 100644 +--- a/Lib/wsgiref/headers.py ++++ b/Lib/wsgiref/headers.py +@@ -9,6 +9,7 @@ + # existence of which force quoting of the parameter value. + import re + tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') ++_control_chars_re = re.compile(r'[\x00-\x1F\x7F]') + + def _formatparam(param, value=None, quote=1): + """Convenience function to format and return a key=value pair. +@@ -41,6 +42,8 @@ def __init__(self, headers=None): + def _convert_string_type(self, value): + """Convert/check value type.""" + if type(value) is str: ++ if _control_chars_re.search(value): ++ raise ValueError("Control characters not allowed in headers") + return value + raise AssertionError("Header names/values must be" + " of type str (got {0})".format(repr(value))) +diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst +new file mode 100644 +index 0000000000..44bd0b2705 +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst +@@ -0,0 +1,2 @@ ++Reject C0 control characters within wsgiref.headers.Headers fields, values, ++and parameters. diff --git a/00474-cve-2025-15366.patch b/00474-cve-2025-15366.patch new file mode 100644 index 0000000..0b4fd18 --- /dev/null +++ b/00474-cve-2025-15366.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Seth Michael Larson +Date: Tue, 20 Jan 2026 14:45:42 -0600 +Subject: 00474: CVE-2025-15366 + +gh-143921: Reject control characters in IMAP commands + +(cherry-picked from commit 6262704b134db2a4ba12e85ecfbd968534f28b45) +--- + Lib/imaplib.py | 4 +++- + Lib/test/test_imaplib.py | 6 ++++++ + .../Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst | 1 + + 3 files changed, 10 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst + +diff --git a/Lib/imaplib.py b/Lib/imaplib.py +index 54122f985b..9ab8e43bbf 100644 +--- a/Lib/imaplib.py ++++ b/Lib/imaplib.py +@@ -132,7 +132,7 @@ + # We compile these in _mode_xxx. + _Literal = br'.*{(?P\d+)}$' + _Untagged_status = br'\* (?P\d+) (?P[A-Z-]+)( (?P.*))?' +- ++_control_chars = re.compile(b'[\x00-\x1F\x7F]') + + + class IMAP4: +@@ -994,6 +994,8 @@ def _command(self, name, *args): + if arg is None: continue + if isinstance(arg, str): + arg = bytes(arg, self._encoding) ++ if _control_chars.search(arg): ++ raise ValueError("Control characters not allowed in commands") + data = data + b' ' + arg + + literal = self.literal +diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py +index f817707743..9ce59b3040 100644 +--- a/Lib/test/test_imaplib.py ++++ b/Lib/test/test_imaplib.py +@@ -505,6 +505,12 @@ def test_login(self): + self.assertEqual(data[0], b'LOGIN completed') + self.assertEqual(client.state, 'AUTH') + ++ def test_control_characters(self): ++ client, _ = self._setup(SimpleIMAPHandler) ++ for c0 in support.control_characters_c0(): ++ with self.assertRaises(ValueError): ++ client.login(f'user{c0}', 'pass') ++ + def test_logout(self): + client, _ = self._setup(SimpleIMAPHandler) + typ, data = client.login('user', 'pass') +diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst +new file mode 100644 +index 0000000000..4e13fe92bc +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst +@@ -0,0 +1 @@ ++Reject control characters in IMAP commands. diff --git a/00475-cve-2025-15367.patch b/00475-cve-2025-15367.patch new file mode 100644 index 0000000..9a64894 --- /dev/null +++ b/00475-cve-2025-15367.patch @@ -0,0 +1,61 @@ +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 + +gh-143923: Reject control characters in POP3 commands + +(cherry-picked from commit b234a2b67539f787e191d2ef19a7cbdce32874e7) +--- + 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 0f8587317c..f563030f7f 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 1220ca32ef..2655e366fd 100644 +--- a/Lib/test/test_poplib.py ++++ b/Lib/test/test_poplib.py +@@ -12,6 +12,7 @@ + import unittest + from unittest import TestCase, skipUnless + from test import support as test_support ++from test.support import control_characters_c0 + from test.support import hashlib_helper + from test.support import socket_helper + from test.support import threading_helper +@@ -365,6 +366,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/python3.10.spec b/python3.10.spec index de79e10..0a4531b 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 4%{?dist} License: Python-2.0.1 @@ -350,6 +350,31 @@ Patch452: 00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch # stressed on OpenSSL 3.5. Patch462: 00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch +# 00473 # e0df4af4663a200a370a7c7b2391588396bcf20f +# CVE-2026-0865 +# +# gh-143916: Reject control characters in wsgiref.headers.Headers (GH-143917) +# +# * Add 'test.support' fixture for C0 control characters +# * gh-143916: Reject control characters in wsgiref.headers.Headers +Patch473: 00473-cve-2026-0865.patch + +# 00474 # 837ddca0372fa87ff9cee47142200caa21e77def +# CVE-2025-15366 +# +# gh-143921: Reject control characters in IMAP commands +# +# (cherry-picked from commit 6262704b134db2a4ba12e85ecfbd968534f28b45) +Patch474: 00474-cve-2025-15366.patch + +# 00475 # 3748209a316662d4e85981ca1a7418547a1d25c6 +# CVE-2025-15367 +# +# gh-143923: Reject control characters in POP3 commands +# +# (cherry-picked from commit b234a2b67539f787e191d2ef19a7cbdce32874e7) +Patch475: 00475-cve-2025-15367.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1649,6 +1674,9 @@ CheckPython optimized # ====================================================== %changelog +* Mon Feb 09 2026 Tomáš Hrnčiar - 3.10.19-4 +- Security fixes for CVE-2026-0865, CVE-2025-15366 and CVE-2025-15367 + * Sat Jan 17 2026 Fedora Release Engineering - 3.10.19-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild From c9e6166456a3fea8d2a44bb6ecdb528bd274e8e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Tue, 3 Mar 2026 14:17:40 +0100 Subject: [PATCH 46/53] Update to 3.10.20 --- ..._seterror-handling-ssl_error_syscall.patch | 2 +- 00473-cve-2026-0865.patch | 90 ------------------- python3.10.spec | 16 ++-- sources | 4 +- 4 files changed, 8 insertions(+), 104 deletions(-) delete mode 100644 00473-cve-2026-0865.patch diff --git a/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch b/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch index 5c0cda1..fcf142f 100644 --- a/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch +++ b/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch @@ -102,7 +102,7 @@ index 0000000000..75d926ab59 +Fix the :mod:`ssl` module error handling of connection terminate by peer. +It now throws an OSError with the appropriate error code instead of an EOFError. diff --git a/Modules/_ssl.c b/Modules/_ssl.c -index 6852cc363b..91bf27bfef 100644 +index 3bd8b96dcc..6f5fb23b8b 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -582,7 +582,7 @@ PySSL_ChainExceptions(PySSLSocket *sslsock) { diff --git a/00473-cve-2026-0865.patch b/00473-cve-2026-0865.patch deleted file mode 100644 index 3504e6b..0000000 --- a/00473-cve-2026-0865.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Seth Michael Larson -Date: Sat, 17 Jan 2026 11:46:21 -0600 -Subject: 00473: CVE-2026-0865 - - gh-143916: Reject control characters in wsgiref.headers.Headers (GH-143917) - -* Add 'test.support' fixture for C0 control characters -* gh-143916: Reject control characters in wsgiref.headers.Headers ---- - Lib/test/support/__init__.py | 7 +++++++ - Lib/test/test_wsgiref.py | 12 +++++++++++- - Lib/wsgiref/headers.py | 3 +++ - .../2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst | 2 ++ - 4 files changed, 23 insertions(+), 1 deletion(-) - create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst - -diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index 0d3b9634f1..d0492fe191 100644 ---- a/Lib/test/support/__init__.py -+++ b/Lib/test/support/__init__.py -@@ -2157,3 +2157,10 @@ def adjust_int_max_str_digits(max_digits): - yield - finally: - sys.set_int_max_str_digits(current) -+ -+ -+def control_characters_c0() -> list[str]: -+ """Returns a list of C0 control characters as strings. -+ C0 control characters defined as the byte range 0x00-0x1F, and 0x7F. -+ """ -+ return [chr(c) for c in range(0x00, 0x20)] + ["\x7F"] -diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py -index 42094f4677..01ca51ba45 100644 ---- a/Lib/test/test_wsgiref.py -+++ b/Lib/test/test_wsgiref.py -@@ -1,6 +1,6 @@ - from unittest import mock - from test import support --from test.support import socket_helper -+from test.support import socket_helper, control_characters_c0 - from test.support import warnings_helper - from test.test_httpservers import NoLogRequestHandler - from unittest import TestCase -@@ -527,6 +527,16 @@ def testExtras(self): - '\r\n' - ) - -+ def testRaisesControlCharacters(self): -+ headers = Headers() -+ for c0 in control_characters_c0(): -+ self.assertRaises(ValueError, headers.__setitem__, f"key{c0}", "val") -+ self.assertRaises(ValueError, headers.__setitem__, "key", f"val{c0}") -+ self.assertRaises(ValueError, headers.add_header, f"key{c0}", "val", param="param") -+ self.assertRaises(ValueError, headers.add_header, "key", f"val{c0}", param="param") -+ self.assertRaises(ValueError, headers.add_header, "key", "val", param=f"param{c0}") -+ -+ - class ErrorHandler(BaseCGIHandler): - """Simple handler subclass for testing BaseHandler""" - -diff --git a/Lib/wsgiref/headers.py b/Lib/wsgiref/headers.py -index fab851c5a4..fd98e85d75 100644 ---- a/Lib/wsgiref/headers.py -+++ b/Lib/wsgiref/headers.py -@@ -9,6 +9,7 @@ - # existence of which force quoting of the parameter value. - import re - tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') -+_control_chars_re = re.compile(r'[\x00-\x1F\x7F]') - - def _formatparam(param, value=None, quote=1): - """Convenience function to format and return a key=value pair. -@@ -41,6 +42,8 @@ def __init__(self, headers=None): - def _convert_string_type(self, value): - """Convert/check value type.""" - if type(value) is str: -+ if _control_chars_re.search(value): -+ raise ValueError("Control characters not allowed in headers") - return value - raise AssertionError("Header names/values must be" - " of type str (got {0})".format(repr(value))) -diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst -new file mode 100644 -index 0000000000..44bd0b2705 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst -@@ -0,0 +1,2 @@ -+Reject C0 control characters within wsgiref.headers.Headers fields, values, -+and parameters. diff --git a/python3.10.spec b/python3.10.spec index 0a4531b..ddee0fe 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.19 +%global general_version %{pybasever}.20 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 4%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -350,15 +350,6 @@ Patch452: 00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch # stressed on OpenSSL 3.5. Patch462: 00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch -# 00473 # e0df4af4663a200a370a7c7b2391588396bcf20f -# CVE-2026-0865 -# -# gh-143916: Reject control characters in wsgiref.headers.Headers (GH-143917) -# -# * Add 'test.support' fixture for C0 control characters -# * gh-143916: Reject control characters in wsgiref.headers.Headers -Patch473: 00473-cve-2026-0865.patch - # 00474 # 837ddca0372fa87ff9cee47142200caa21e77def # CVE-2025-15366 # @@ -1674,6 +1665,9 @@ CheckPython optimized # ====================================================== %changelog +* Tue Mar 03 2026 Tomáš Hrnčiar - 3.10.20-1 +- Update to 3.10.20 + * Mon Feb 09 2026 Tomáš Hrnčiar - 3.10.19-4 - Security fixes for CVE-2026-0865, CVE-2025-15366 and CVE-2025-15367 diff --git a/sources b/sources index 6de6cda..073dea3 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.19.tar.xz) = 9ea1e952bb5f7c9cc8d3efd8e7ae0059c129b6e09d12ded7d4e22476fad35f2c8186b1e003ea35a0099ffa5ff0a114985c3e960fba84e7037670b840a3e19c68 -SHA512 (Python-3.10.19.tar.xz.asc) = 64bcee68c2f4e0718daaf19d405f4a5bd0ce3fc3d13bb5cbae8d4a36e419dd104c94323b497f1d06110f6b62a1b3b351279c534145f4e8cea9af0d7bb0a8b5c9 +SHA512 (Python-3.10.20.tar.xz) = 591d3acf7ef47307b8c58fec9516731b374bcdf6eb21883410f67402f823a183f1abffec1116583e394610730f414d5a98b4c3437ea5174a4d2adbb99b8f6e5b +SHA512 (Python-3.10.20.tar.xz.asc) = cb90ce514c63c63adc10cbb3db778eae626b922461715954daf08b3a3a99b8b4c750fded624787b7d28fc10a203b0e0e5494b91e5305ef56a23fda46c023c203 From 458effe6fa67601a6c8dbc89c8ea31630a0cdb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 9 Apr 2026 13:16:25 +0200 Subject: [PATCH 47/53] Explicitly build with OpenSSL 3 for now https://fedoraproject.org/wiki/Changes/OpenSSL40 --- python3.10.spec | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/python3.10.spec b/python3.10.spec index ddee0fe..b076721 100644 --- a/python3.10.spec +++ b/python3.10.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 @@ -216,7 +216,6 @@ BuildRequires: make BuildRequires: mpdecimal-devel BuildRequires: ncurses-devel -BuildRequires: openssl-devel BuildRequires: pkgconfig BuildRequires: readline-devel BuildRequires: redhat-rpm-config >= 127 @@ -229,6 +228,10 @@ BuildRequires: tix-devel BuildRequires: tk-devel < 1:9 BuildRequires: tzdata +# 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 %{with valgrind} BuildRequires: valgrind-devel %endif @@ -1665,6 +1668,9 @@ CheckPython optimized # ====================================================== %changelog +* Sat Apr 11 2026 Miro Hrončok - 3.10.20-2 +- Explicitly build with OpenSSL 3 + * Tue Mar 03 2026 Tomáš Hrnčiar - 3.10.20-1 - Update to 3.10.20 From 16aa6aa9ffefb7c724be998444c3d6a41f393827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 2 Jul 2026 10:59:38 +0200 Subject: [PATCH 48/53] Fix ssl.SSLError: [ASN1: NOT_ENOUGH_DATA] not enough data with OpenSSL 3.5.7+ --- 00489-openssl-3.5.7.patch | 75 +++++++++++++++++++++++++++++++++++++++ python3.10.spec | 13 +++++++ 2 files changed, 88 insertions(+) create mode 100644 00489-openssl-3.5.7.patch diff --git a/00489-openssl-3.5.7.patch b/00489-openssl-3.5.7.patch new file mode 100644 index 0000000..ea5bf03 --- /dev/null +++ b/00489-openssl-3.5.7.patch @@ -0,0 +1,75 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: David Benjamin +Date: Fri, 24 Mar 2023 09:04:30 -0400 +Subject: 00489: Use BIO_eof to detect EOF for SSL_FILETYPE_ASN1 + +In PEM, we need to parse until error and then suppress `PEM_R_NO_START_LINE`, because PEM allows arbitrary leading and trailing data. DER, however, does not. Parsing until error and suppressing `ASN1_R_HEADER_TOO_LONG` doesn't quite work because that error also covers some cases that should be rejected. + +Instead, check `BIO_eof` early and stop the loop that way. + +This fixes https://github.com/python/cpython/issues/151504 and adds compatibility with OpenSSL 3.5.7+ + +(cherry-picked from commit acfe02f3b05436658d92add6b168538b30f357f0) +--- + Lib/test/test_ssl.py | 2 ++ + .../2022-12-20-10-55-14.gh-issue-100372.utfP65.rst | 2 ++ + Modules/_ssl.c | 10 ++++++---- + 3 files changed, 10 insertions(+), 4 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst + +diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py +index 21d3676c22..f772bd6ab6 100644 +--- a/Lib/test/test_ssl.py ++++ b/Lib/test/test_ssl.py +@@ -1533,6 +1533,8 @@ def test_load_verify_cadata(self): + "not enough data: cadata does not contain a certificate" + ): + ctx.load_verify_locations(cadata=b"broken") ++ with self.assertRaises(ssl.SSLError): ++ ctx.load_verify_locations(cadata=cacert_der + b"A") + + @unittest.skipIf(Py_DEBUG_WIN32, "Avoid mixing debug/release CRT on Windows") + def test_load_dh_params(self): +diff --git a/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst b/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst +new file mode 100644 +index 0000000000..ec37aff509 +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst +@@ -0,0 +1,2 @@ ++:meth:`ssl.SSLContext.load_verify_locations` no longer incorrectly accepts ++some cases of trailing data when parsing DER. +diff --git a/Modules/_ssl.c b/Modules/_ssl.c +index 6f5fb23b8b..7b3cf61474 100644 +--- a/Modules/_ssl.c ++++ b/Modules/_ssl.c +@@ -3949,7 +3949,7 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len, + { + BIO *biobuf = NULL; + X509_STORE *store; +- int retval = -1, err, loaded = 0; ++ int retval = -1, err, loaded = 0, was_bio_eof = 0; + + assert(filetype == SSL_FILETYPE_ASN1 || filetype == SSL_FILETYPE_PEM); + +@@ -3977,6 +3977,10 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len, + int r; + + if (filetype == SSL_FILETYPE_ASN1) { ++ if (BIO_eof(biobuf)) { ++ was_bio_eof = 1; ++ break; ++ } + cert = d2i_X509_bio(biobuf, NULL); + } else { + cert = PEM_read_bio_X509(biobuf, NULL, +@@ -4012,9 +4016,7 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len, + } + _setSSLError(get_state_ctx(self), msg, 0, __FILE__, __LINE__); + retval = -1; +- } else if ((filetype == SSL_FILETYPE_ASN1) && +- (ERR_GET_LIB(err) == ERR_LIB_ASN1) && +- (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG)) { ++ } else if ((filetype == SSL_FILETYPE_ASN1) && was_bio_eof) { + /* EOF ASN1 file, not an error */ + ERR_clear_error(); + retval = 0; diff --git a/python3.10.spec b/python3.10.spec index b076721..53f3806 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -369,6 +369,18 @@ Patch474: 00474-cve-2025-15366.patch # (cherry-picked from commit b234a2b67539f787e191d2ef19a7cbdce32874e7) Patch475: 00475-cve-2025-15367.patch +# 00489 # 008af720a5f6f98ed3feb8ebdbf88ab9dea4db22 +# Use BIO_eof to detect EOF for SSL_FILETYPE_ASN1 +# +# In PEM, we need to parse until error and then suppress `PEM_R_NO_START_LINE`, because PEM allows arbitrary leading and trailing data. DER, however, does not. Parsing until error and suppressing `ASN1_R_HEADER_TOO_LONG` doesn't quite work because that error also covers some cases that should be rejected. +# +# Instead, check `BIO_eof` early and stop the loop that way. +# +# This fixes https://github.com/python/cpython/issues/151504 and adds compatibility with OpenSSL 3.5.7+ +# +# (cherry-picked from commit acfe02f3b05436658d92add6b168538b30f357f0) +Patch489: 00489-openssl-3.5.7.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1670,6 +1682,7 @@ CheckPython optimized %changelog * Sat Apr 11 2026 Miro Hrončok - 3.10.20-2 - Explicitly build with OpenSSL 3 +- Fix ssl.SSLError: [ASN1: NOT_ENOUGH_DATA] not enough data with OpenSSL 3.5.7+ * Tue Mar 03 2026 Tomáš Hrnčiar - 3.10.20-1 - Update to 3.10.20 From e6d9413cd5d26f89148ae857642b9a1921465822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lum=C3=ADr=20Balhar?= Date: Wed, 1 Jul 2026 10:55:23 +0000 Subject: [PATCH 49/53] Run a new test to monitor changes in required symbols Co-Authored-By: Claude Sonnet 4.6 (1M context) --- plan.fmf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plan.fmf b/plan.fmf index 71f3e63..88053e4 100644 --- a/plan.fmf +++ b/plan.fmf @@ -34,6 +34,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 @@ -50,8 +53,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 From c30eb404789121d3ed86e736ccdf5952df55279a Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 16 Jul 2026 22:47:20 +0000 Subject: [PATCH 50/53] Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild --- python3.10.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3.10.spec b/python3.10.spec index 53f3806..85c416e 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 2%{?dist} +Release: 3%{?dist} License: Python-2.0.1 @@ -1680,6 +1680,9 @@ CheckPython optimized # ====================================================== %changelog +* Thu Jul 16 2026 Fedora Release Engineering - 3.10.20-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild + * Sat Apr 11 2026 Miro Hrončok - 3.10.20-2 - Explicitly build with OpenSSL 3 - Fix ssl.SSLError: [ASN1: NOT_ENOUGH_DATA] not enough data with OpenSSL 3.5.7+ From 7da201e90094c47501dcb1ee89872aef1884f4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 30 Jul 2026 17:25:58 +0200 Subject: [PATCH 51/53] Skip UDP Lite tests if it's not supported - Fixes FTBFS on Linux kernel 7.1 and newer --- ...udp-lite-tests-if-it-s-not-supported.patch | 64 +++++++++++++++++++ python3.10.spec | 13 +++- 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch diff --git a/00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch b/00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch new file mode 100644 index 0000000..bf0a459 --- /dev/null +++ b/00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Victor Stinner +Date: Wed, 13 May 2026 17:27:56 +0200 +Subject: 00491: gh-149776: Skip UDP Lite tests if it's not supported + +Fix test_socket on Linux kernel 7.1 and newer: skip UDP Lite tests if +it's not supported. + +(cherry picked from commit 3cfc249e11a132dc69624150843779aa96c72b2b) +(cherry picked from commit 49d08674d8dba50dc29539e3c7bce21d66066b06) +--- + Lib/test/test_socket.py | 22 ++++++++++++++++++- + ...-05-13-14-53-23.gh-issue-149776.orqgsn.rst | 2 ++ + 2 files changed, 23 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst + +diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py +index 15f7fb4a80..4a3d8f7ce6 100644 +--- a/Lib/test/test_socket.py ++++ b/Lib/test/test_socket.py +@@ -142,6 +142,26 @@ def _have_socket_bluetooth(): + return True + + ++def _have_udp_lite(): ++ if not hasattr(socket, "IPPROTO_UDPLITE"): ++ return False ++ # Older Android versions block UDPLITE with SELinux. ++ if support.is_android and platform.android_ver().api_level < 29: ++ return False ++ ++ try: ++ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDPLITE) ++ except OSError as exc: ++ # Linux 7.1 removed UDP Lite support ++ if exc.errno == errno.EPROTONOSUPPORT: ++ return False ++ raise ++ sock.close() ++ ++ return True ++ ++ ++ + @contextlib.contextmanager + def socket_setdefaulttimeout(timeout): + old_timeout = socket.getdefaulttimeout() +@@ -166,7 +186,7 @@ def socket_setdefaulttimeout(timeout): + + HAVE_SOCKET_VSOCK = _have_socket_vsock() + +-HAVE_SOCKET_UDPLITE = hasattr(socket, "IPPROTO_UDPLITE") ++HAVE_SOCKET_UDPLITE = _have_udp_lite() + + HAVE_SOCKET_BLUETOOTH = _have_socket_bluetooth() + +diff --git a/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst b/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst +new file mode 100644 +index 0000000000..e86a9130ff +--- /dev/null ++++ b/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst +@@ -0,0 +1,2 @@ ++Fix test_socket on Linux kernel 7.1 and newer: skip UDP Lite tests if it's ++not supported. Patch by Victor Stinner. diff --git a/python3.10.spec b/python3.10.spec index 85c416e..d06a2f6 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -17,7 +17,7 @@ URL: https://www.python.org/ #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 3%{?dist} +Release: 4%{?dist} License: Python-2.0.1 @@ -381,6 +381,13 @@ Patch475: 00475-cve-2025-15367.patch # (cherry-picked from commit acfe02f3b05436658d92add6b168538b30f357f0) Patch489: 00489-openssl-3.5.7.patch +# 00491 # ac14737379922303720216b61803474c84f291ef +# gh-149776: Skip UDP Lite tests if it's not supported +# +# Fix test_socket on Linux kernel 7.1 and newer: skip UDP Lite tests if +# it's not supported. +Patch491: 00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1680,6 +1687,10 @@ CheckPython optimized # ====================================================== %changelog +* Thu Jul 30 2026 Miro Hrončok - 3.10.20-4 + - Skip UDP Lite tests if it's not supported + - Fixes FTBFS on Linux kernel 7.1 and newer + * Thu Jul 16 2026 Fedora Release Engineering - 3.10.20-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild From b4205523e70d79d5be55bc4d9368bbe1cbceddcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 11 Mar 2026 19:39:18 +0100 Subject: [PATCH 52/53] Only explicitly require expat >= installed version when expat < 2.7.4 See https://src.fedoraproject.org/rpms/expat/c/4da0543472 (cherry picked from python3.15 commit ce1bde3e67443b7cf5df33bf58cb2ec75cc2c8e2) --- python3.10.spec | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python3.10.spec b/python3.10.spec index d06a2f6..97d0e29 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -545,8 +545,12 @@ Requires: tzdata # We avoid this problem by requiring at least the same version of expat that # was used during the build time. # 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}' expat.%{_target_cpu} | sed 's/.*not installed/0/') +%if v"%{expat_version}" < v"2.7.4" Requires: expat%{?_isa} >= %{expat_version} +%endif # Since patch 251 changed from distutils to sysconfig, pip needed to be adapted From daba5b738caf36d3903f98c1c65be5efbcfa658a Mon Sep 17 00:00:00 2001 From: Karolina Surma Date: Thu, 13 Aug 2026 12:14:24 +0200 Subject: [PATCH 53/53] Update to Python 3.10.21 --- ..._seterror-handling-ssl_error_syscall.patch | 10 +-- 00489-openssl-3.5.7.patch | 75 ------------------- ...udp-lite-tests-if-it-s-not-supported.patch | 64 ---------------- ...-test_large_content_length_truncated.patch | 23 ++++++ python3.10.spec | 28 +++---- sources | 4 +- 6 files changed, 39 insertions(+), 165 deletions(-) delete mode 100644 00489-openssl-3.5.7.patch delete mode 100644 00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch create mode 100644 00494-increase-the-timeout-of-test_large_content_length_truncated.patch diff --git a/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch b/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch index fcf142f..1eaf3a2 100644 --- a/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch +++ b/00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch @@ -22,10 +22,10 @@ Co-authored-by: Petr Viktorin create mode 100644 Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py -index 0f1397de73..21d3676c22 100644 +index f4bba8ff03..f772bd6ab6 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py -@@ -2605,16 +2605,18 @@ def run(self): +@@ -2607,16 +2607,18 @@ def run(self): self.write(msg.lower()) except OSError as e: # handles SSLError and socket errors @@ -53,7 +53,7 @@ index 0f1397de73..21d3676c22 100644 try: self.write(b"ERROR\n") except OSError: -@@ -3296,23 +3298,16 @@ def test_wrong_cert_tls13(self): +@@ -3298,23 +3300,16 @@ def test_wrong_cert_tls13(self): client_context.wrap_socket(socket.socket(), server_hostname=hostname, suppress_ragged_eofs=False) as s: @@ -82,7 +82,7 @@ index 0f1397de73..21d3676c22 100644 def test_rude_shutdown(self): """A brutal shutdown of an SSL server should raise an OSError -@@ -4558,8 +4553,8 @@ def msg_cb(conn, direction, version, content_type, msg_type, data): +@@ -4560,8 +4555,8 @@ def msg_cb(conn, direction, version, content_type, msg_type, data): # test sometimes fails with EOF error. Test passes as long as # server aborts connection with an error. with self.assertRaisesRegex( @@ -102,7 +102,7 @@ index 0000000000..75d926ab59 +Fix the :mod:`ssl` module error handling of connection terminate by peer. +It now throws an OSError with the appropriate error code instead of an EOFError. diff --git a/Modules/_ssl.c b/Modules/_ssl.c -index 3bd8b96dcc..6f5fb23b8b 100644 +index af770c7f18..7b3cf61474 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -582,7 +582,7 @@ PySSL_ChainExceptions(PySSLSocket *sslsock) { diff --git a/00489-openssl-3.5.7.patch b/00489-openssl-3.5.7.patch deleted file mode 100644 index ea5bf03..0000000 --- a/00489-openssl-3.5.7.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: David Benjamin -Date: Fri, 24 Mar 2023 09:04:30 -0400 -Subject: 00489: Use BIO_eof to detect EOF for SSL_FILETYPE_ASN1 - -In PEM, we need to parse until error and then suppress `PEM_R_NO_START_LINE`, because PEM allows arbitrary leading and trailing data. DER, however, does not. Parsing until error and suppressing `ASN1_R_HEADER_TOO_LONG` doesn't quite work because that error also covers some cases that should be rejected. - -Instead, check `BIO_eof` early and stop the loop that way. - -This fixes https://github.com/python/cpython/issues/151504 and adds compatibility with OpenSSL 3.5.7+ - -(cherry-picked from commit acfe02f3b05436658d92add6b168538b30f357f0) ---- - Lib/test/test_ssl.py | 2 ++ - .../2022-12-20-10-55-14.gh-issue-100372.utfP65.rst | 2 ++ - Modules/_ssl.c | 10 ++++++---- - 3 files changed, 10 insertions(+), 4 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst - -diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py -index 21d3676c22..f772bd6ab6 100644 ---- a/Lib/test/test_ssl.py -+++ b/Lib/test/test_ssl.py -@@ -1533,6 +1533,8 @@ def test_load_verify_cadata(self): - "not enough data: cadata does not contain a certificate" - ): - ctx.load_verify_locations(cadata=b"broken") -+ with self.assertRaises(ssl.SSLError): -+ ctx.load_verify_locations(cadata=cacert_der + b"A") - - @unittest.skipIf(Py_DEBUG_WIN32, "Avoid mixing debug/release CRT on Windows") - def test_load_dh_params(self): -diff --git a/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst b/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst -new file mode 100644 -index 0000000000..ec37aff509 ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst -@@ -0,0 +1,2 @@ -+:meth:`ssl.SSLContext.load_verify_locations` no longer incorrectly accepts -+some cases of trailing data when parsing DER. -diff --git a/Modules/_ssl.c b/Modules/_ssl.c -index 6f5fb23b8b..7b3cf61474 100644 ---- a/Modules/_ssl.c -+++ b/Modules/_ssl.c -@@ -3949,7 +3949,7 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len, - { - BIO *biobuf = NULL; - X509_STORE *store; -- int retval = -1, err, loaded = 0; -+ int retval = -1, err, loaded = 0, was_bio_eof = 0; - - assert(filetype == SSL_FILETYPE_ASN1 || filetype == SSL_FILETYPE_PEM); - -@@ -3977,6 +3977,10 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len, - int r; - - if (filetype == SSL_FILETYPE_ASN1) { -+ if (BIO_eof(biobuf)) { -+ was_bio_eof = 1; -+ break; -+ } - cert = d2i_X509_bio(biobuf, NULL); - } else { - cert = PEM_read_bio_X509(biobuf, NULL, -@@ -4012,9 +4016,7 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len, - } - _setSSLError(get_state_ctx(self), msg, 0, __FILE__, __LINE__); - retval = -1; -- } else if ((filetype == SSL_FILETYPE_ASN1) && -- (ERR_GET_LIB(err) == ERR_LIB_ASN1) && -- (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG)) { -+ } else if ((filetype == SSL_FILETYPE_ASN1) && was_bio_eof) { - /* EOF ASN1 file, not an error */ - ERR_clear_error(); - retval = 0; diff --git a/00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch b/00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch deleted file mode 100644 index bf0a459..0000000 --- a/00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Victor Stinner -Date: Wed, 13 May 2026 17:27:56 +0200 -Subject: 00491: gh-149776: Skip UDP Lite tests if it's not supported - -Fix test_socket on Linux kernel 7.1 and newer: skip UDP Lite tests if -it's not supported. - -(cherry picked from commit 3cfc249e11a132dc69624150843779aa96c72b2b) -(cherry picked from commit 49d08674d8dba50dc29539e3c7bce21d66066b06) ---- - Lib/test/test_socket.py | 22 ++++++++++++++++++- - ...-05-13-14-53-23.gh-issue-149776.orqgsn.rst | 2 ++ - 2 files changed, 23 insertions(+), 1 deletion(-) - create mode 100644 Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst - -diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 15f7fb4a80..4a3d8f7ce6 100644 ---- a/Lib/test/test_socket.py -+++ b/Lib/test/test_socket.py -@@ -142,6 +142,26 @@ def _have_socket_bluetooth(): - return True - - -+def _have_udp_lite(): -+ if not hasattr(socket, "IPPROTO_UDPLITE"): -+ return False -+ # Older Android versions block UDPLITE with SELinux. -+ if support.is_android and platform.android_ver().api_level < 29: -+ return False -+ -+ try: -+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDPLITE) -+ except OSError as exc: -+ # Linux 7.1 removed UDP Lite support -+ if exc.errno == errno.EPROTONOSUPPORT: -+ return False -+ raise -+ sock.close() -+ -+ return True -+ -+ -+ - @contextlib.contextmanager - def socket_setdefaulttimeout(timeout): - old_timeout = socket.getdefaulttimeout() -@@ -166,7 +186,7 @@ def socket_setdefaulttimeout(timeout): - - HAVE_SOCKET_VSOCK = _have_socket_vsock() - --HAVE_SOCKET_UDPLITE = hasattr(socket, "IPPROTO_UDPLITE") -+HAVE_SOCKET_UDPLITE = _have_udp_lite() - - HAVE_SOCKET_BLUETOOTH = _have_socket_bluetooth() - -diff --git a/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst b/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst -new file mode 100644 -index 0000000000..e86a9130ff ---- /dev/null -+++ b/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst -@@ -0,0 +1,2 @@ -+Fix test_socket on Linux kernel 7.1 and newer: skip UDP Lite tests if it's -+not supported. Patch by Victor Stinner. diff --git a/00494-increase-the-timeout-of-test_large_content_length_truncated.patch b/00494-increase-the-timeout-of-test_large_content_length_truncated.patch new file mode 100644 index 0000000..58ffd5d --- /dev/null +++ b/00494-increase-the-timeout-of-test_large_content_length_truncated.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Karolina Surma +Date: Fri, 14 Aug 2026 09:38:26 +0200 +Subject: 00494: Increase the timeout of test_large_content_length_truncated + +It has started to fail randomly when run on s390x architecture. +--- + Lib/test/test_httpservers.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py +index 5eb3c82fbd..aa8ad1c323 100644 +--- a/Lib/test/test_httpservers.py ++++ b/Lib/test/test_httpservers.py +@@ -872,7 +872,7 @@ def test_large_content_length(self): + self.assertEqual(res.read(), b'%d %d' % (size, size) + self.linesep) + + def test_large_content_length_truncated(self): +- with support.swap_attr(self.request_handler, 'timeout', 0.001): ++ with support.swap_attr(self.request_handler, 'timeout', support.LOOPBACK_TIMEOUT): + for w in range(18, 65): + size = 1 << w + headers = {'Content-Length' : str(size)} diff --git a/python3.10.spec b/python3.10.spec index 97d0e29..3378bf7 100644 --- a/python3.10.spec +++ b/python3.10.spec @@ -13,11 +13,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}.20 +%global general_version %{pybasever}.21 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 4%{?dist} +Release: 1%{?dist} License: Python-2.0.1 @@ -369,24 +369,11 @@ Patch474: 00474-cve-2025-15366.patch # (cherry-picked from commit b234a2b67539f787e191d2ef19a7cbdce32874e7) Patch475: 00475-cve-2025-15367.patch -# 00489 # 008af720a5f6f98ed3feb8ebdbf88ab9dea4db22 -# Use BIO_eof to detect EOF for SSL_FILETYPE_ASN1 +# 00494 # 430aab133397ed44cc9ee621fd311e02fee317b5 +# Increase the timeout of test_large_content_length_truncated # -# In PEM, we need to parse until error and then suppress `PEM_R_NO_START_LINE`, because PEM allows arbitrary leading and trailing data. DER, however, does not. Parsing until error and suppressing `ASN1_R_HEADER_TOO_LONG` doesn't quite work because that error also covers some cases that should be rejected. -# -# Instead, check `BIO_eof` early and stop the loop that way. -# -# This fixes https://github.com/python/cpython/issues/151504 and adds compatibility with OpenSSL 3.5.7+ -# -# (cherry-picked from commit acfe02f3b05436658d92add6b168538b30f357f0) -Patch489: 00489-openssl-3.5.7.patch - -# 00491 # ac14737379922303720216b61803474c84f291ef -# gh-149776: Skip UDP Lite tests if it's not supported -# -# Fix test_socket on Linux kernel 7.1 and newer: skip UDP Lite tests if -# it's not supported. -Patch491: 00491-gh-149776-skip-udp-lite-tests-if-it-s-not-supported.patch +# It has started to fail randomly when run on s390x architecture. +Patch494: 00494-increase-the-timeout-of-test_large_content_length_truncated.patch # (New patches go here ^^^) # @@ -1691,6 +1678,9 @@ CheckPython optimized # ====================================================== %changelog +* Thu Aug 13 2026 Karolina Surma - 3.10.21-1 +- Update to Python 3.10.21 + * Thu Jul 30 2026 Miro Hrončok - 3.10.20-4 - Skip UDP Lite tests if it's not supported - Fixes FTBFS on Linux kernel 7.1 and newer diff --git a/sources b/sources index 073dea3..1833fd0 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.10.20.tar.xz) = 591d3acf7ef47307b8c58fec9516731b374bcdf6eb21883410f67402f823a183f1abffec1116583e394610730f414d5a98b4c3437ea5174a4d2adbb99b8f6e5b -SHA512 (Python-3.10.20.tar.xz.asc) = cb90ce514c63c63adc10cbb3db778eae626b922461715954daf08b3a3a99b8b4c750fded624787b7d28fc10a203b0e0e5494b91e5305ef56a23fda46c023c203 +SHA512 (Python-3.10.21.tar.xz) = 6f6de7c5e4c0457f2d189ed5d111c83fb8775e19123afe4f9fd0ae2b93f3fa2bbb7ad849ade6bb5227d4a6a3d63abc8e167fbe5fb54fa715660c89fd6274daee +SHA512 (Python-3.10.21.tar.xz.asc) = 72d6aeaa0f51e527f82392cf1f969b0ad50a48fe10e8b7e5feb6117718223d00757f7b375b3ac82e257571d5a61f8162e9dd59b81fcf8f2a441c6deb42be71cb