From 8964b106df6258522c55f5545fecc6459a18109a Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Mon, 2 Nov 2026 12:28:47 -0700 Subject: [PATCH] Patch CPython 3.14.10 FIPS regression in _hashopenssl.c CPython 3.14.10 shipped a bad backport of upstream gh-149816 (CPython PR #160006, merged 2026-10-21). The main-branch fix landed as ``int fips = !disable_fips_property(py_ht);`` in ``Modules/_hashopenssl.c::py_digest_by_name``, but the 3.14 backport dropped the leading ``!``, inverting the FIPS property polarity: * ``openssl_md5(usedforsecurity=False)`` under FIPS now fetches MD5 without the ``-fips`` property filter, so OpenSSL can't find a non-FIPS MD5 and the probe raises. hashlib.py's dispatch then falls back to the builtin ``_md5`` (compiled in via ``--with-builtin-hashlib-hashes=blake2,md5,sha1,sha2,sha3``), which has no FIPS awareness and silently returns an MD5 digest. * Conversely, ``openssl_md5(usedforsecurity=True)`` would fetch with the ``-fips`` filter and succeed in FIPS mode via a non-FIPS provider, silently bypassing FIPS enforcement. Caught by ``Test FIPS compatability (3.14, x86_64)`` on PR #338 after the Python 3.14.9 -> 3.14.10 bump; 3.10-3.13 and 3.14.9 pass, 3.14.10 fails. Apply the one-character fix as a build-time patch across linux/darwin/ windows. The regex matches the broken form only -- a correctly-fixed source has ``!disable_fips_property`` and the regex won't match, so this is idempotent and self-disabling. Remove once a 3.14 point release ships the real fix. Refs: - CPython main fix: https://github.com/python/cpython/commit/23469818538c - 3.14 bad backport: https://github.com/python/cpython/pull/160006 --- relenv/build/darwin.py | 14 ++++++++++++++ relenv/build/linux.py | 14 ++++++++++++++ relenv/build/windows.py | 13 +++++++++++++ 3 files changed, 41 insertions(+) diff --git a/relenv/build/darwin.py b/relenv/build/darwin.py index 228ac22f..9f164575 100644 --- a/relenv/build/darwin.py +++ b/relenv/build/darwin.py @@ -25,6 +25,7 @@ builds, finalize, get_dependency_version, + patch_file, update_sbom_checksums, ) @@ -150,6 +151,19 @@ def build_python(env: MutableMapping[str, str], dirs: Dirs, logfp: IO[str]) -> N # Update bundled expat to latest version update_expat(dirs, env) + # CPython 3.14.10 shipped a bad backport of gh-149816 (upstream PR #160006) + # that dropped the "!" from `int fips = !disable_fips_property(py_ht);` in + # Modules/_hashopenssl.c. The inverted polarity makes openssl_md5( + # usedforsecurity=False) fail under FIPS, so hashlib falls back to the + # builtin MD5 and silently bypasses FIPS enforcement. Idempotent: the + # correctly-fixed source has "!disable_fips_property" which this regex + # does not match. Remove once a CPython 3.14 point release ships the fix. + patch_file( + dirs.source / "Modules" / "_hashopenssl.c", + r"int fips = disable_fips_property\(", + "int fips = !disable_fips_property(", + ) + env["LDFLAGS"] = "-Wl,-rpath,{prefix}/lib {ldflags}".format(prefix=dirs.prefix, ldflags=env["LDFLAGS"]) runcmd( [ diff --git a/relenv/build/linux.py b/relenv/build/linux.py index 775b8326..729c451d 100644 --- a/relenv/build/linux.py +++ b/relenv/build/linux.py @@ -28,6 +28,7 @@ builds, finalize, get_dependency_version, + patch_file, update_sbom_checksums, ) @@ -507,6 +508,19 @@ def build_python(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None: # Update bundled expat to latest version update_expat(dirs, env) + # CPython 3.14.10 shipped a bad backport of gh-149816 (upstream PR #160006) + # that dropped the "!" from `int fips = !disable_fips_property(py_ht);` in + # Modules/_hashopenssl.c. The inverted polarity makes openssl_md5( + # usedforsecurity=False) fail under FIPS, so hashlib falls back to the + # builtin MD5 and silently bypasses FIPS enforcement. Idempotent: the + # correctly-fixed source has "!disable_fips_property" which this regex + # does not match. Remove once a CPython 3.14 point release ships the fix. + patch_file( + dirs.source / "Modules" / "_hashopenssl.c", + r"int fips = disable_fips_property\(", + "int fips = !disable_fips_property(", + ) + ldflagopt = f"-Wl,--rpath={dirs.prefix}/lib" if ldflagopt not in env["LDFLAGS"]: env["LDFLAGS"] = f"{ldflagopt} {env['LDFLAGS']}" diff --git a/relenv/build/windows.py b/relenv/build/windows.py index 317c11e0..44af4b8b 100644 --- a/relenv/build/windows.py +++ b/relenv/build/windows.py @@ -1007,6 +1007,19 @@ def build_python(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None: patch_ssl_for_cpython_104135(dirs.source) + # CPython 3.14.10 shipped a bad backport of gh-149816 (upstream PR #160006) + # that dropped the "!" from `int fips = !disable_fips_property(py_ht);` in + # Modules/_hashopenssl.c. The inverted polarity makes openssl_md5( + # usedforsecurity=False) fail under FIPS, so hashlib falls back to the + # builtin MD5 and silently bypasses FIPS enforcement. Idempotent: the + # correctly-fixed source has "!disable_fips_property" which this regex + # does not match. Remove once a CPython 3.14 point release ships the fix. + patch_file( + dirs.source / "Modules" / "_hashopenssl.c", + r"int fips = disable_fips_property\(", + "int fips = !disable_fips_property(", + ) + arch_to_plat = {"amd64": "x64", "x86": "win32", "arm64": "arm64"} arch = env["RELENV_HOST_ARCH"] plat = arch_to_plat[arch]