diff --git a/expat-requires.py b/expat-requires.py new file mode 100755 index 0000000..fe23fa9 --- /dev/null +++ b/expat-requires.py @@ -0,0 +1,50 @@ +import pathlib +import pyexpat +import sys + + +# This will determine the version of currently installed expat +EXPAT_VERSION = pyexpat.EXPAT_VERSION.removeprefix('expat_') +MAJOR, MINOR, PATCH = (int(i) for i in EXPAT_VERSION.split('.')) +EXPAT_COMBINED_VERSION = 10000*MAJOR + 100*MINOR + PATCH + +# For the listed files, we find all XML_COMBINED_VERSION-based #ifs +SRC = pathlib.Path.cwd() +SOURCES = [ + SRC / 'Modules/pyexpat.c', + SRC / 'Modules/clinic/pyexpat.c.h', +] +versions = set() +for source in SOURCES: + for line in source.read_text().splitlines(): + if 'XML_COMBINED_VERSION' not in line: + continue + words = line.split() + if words[0] == '#define': + continue + if len(words) != 4: + continue + if words[0] not in ('#if', '#elif'): + continue + if words[1].startswith('(') and words[-1].endswith(')'): + words[1] = words[1][1:] + words[-1] = words[-1][:-1] + if words[1] == 'XML_COMBINED_VERSION': + version = int(words[3]) + if words[2] == '>': + versions.add(version+1) + continue + if words[2] == '>=': + versions.add(version) + continue + raise ValueError( + 'Unknown line with XML_COMBINED_VERSION, adjust this script:\n\n' + f'{line}' + ) + +# We need the highest satisfiable version used in the #ifs, in x.y.z notation +v = max({v for v in versions if v <= EXPAT_COMBINED_VERSION}) +major, minor_patch = divmod(v, 10000) +minor, patch = divmod(minor_patch, 100) +print(f"{major}.{minor}.{patch}") + diff --git a/python3.12.spec b/python3.12.spec index 48059f4..9bd429d 100644 --- a/python3.12.spec +++ b/python3.12.spec @@ -347,6 +347,9 @@ Source2: https://github.com/Yhg1s.gpg # Originally written by bkabrda Source8: check-pyc-timestamps.py +# A script that determines the required expat version +Source9: expat-requires.py + # Desktop menu entry for idle3 Source10: idle3.desktop @@ -614,12 +617,13 @@ 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 newer expat, 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 +%global expat_min_version 2.7.2 +Requires: expat%{_isa} >= %{expat_min_version} %description -n %{pkgname}-libs This package contains runtime libraries for use by Python: @@ -1264,6 +1268,11 @@ for Module in %{buildroot}/%{dynload_dir}/*.so ; do esac done +# Check the expat compatibility +expat_found=$(LD_LIBRARY_PATH="%{buildroot}%{_libdir}" PYTHONPATH="%{buildroot}%{pylibdir}" %{buildroot}%{_bindir}/python%{pybasever} %{SOURCE9}) +if [ "${expat_found}" != "%{expat_min_version}" ]; then + echo "Found expat version is different than the declared one, found: ${expat_found}" ; exit 1 +fi # ====================================================== # Running the upstream test suite @@ -1824,6 +1833,7 @@ CheckPython optimized * Thu Apr 16 2026 Tomáš Hrnčiar - 3.12.13-1 - Update to 3.12.13 - Security fixes for CVE-2025-6075, CVE-2025-13837, CVE-2025-15282, CVE-2025-59375, CVE-2026-0672 +- Require expat >= 2.7.2 to prevent symbol lookup errors at runtime with older expat Related: RHEL-167886, RHEL-168120 * Fri Mar 27 2026 Tomáš Hrnčiar - 3.12.12-6