From 5d81e918b9d5b612f53e1f5ad878a52f42c2871e Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Thu, 25 Jun 2026 11:48:01 -0500 Subject: [PATCH 01/12] add hardening flags to linux targets Enable stack protection and immediate binding for all Linux builds. Enable source fortification for optimized glibc targets. closes #837 --- cpython-unix/build-cpython.sh | 4 ++ cpython-unix/build.py | 8 +++ cpython-unix/targets.yml | 105 ++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 8224aca26..0f61fe24a 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -729,6 +729,10 @@ autoconf # 3.12 and earlier) CFLAGS_JIT="${CFLAGS}" +# JIT stencils cannot call the stack-protector symbols, so let CPython's +# -fno-stack-protector take precedence for their compilation. +CFLAGS_JIT="${CFLAGS_JIT//-fstack-protector-strong/}" + # In 3.14+, the JIT compiler on x86-64 Linux uses a model that conflicts with `-fPIC`, so strip it # from the flags. See: # - https://github.com/python/cpython/issues/135690 diff --git a/cpython-unix/build.py b/cpython-unix/build.py index eb0e6ae86..caba94e85 100755 --- a/cpython-unix/build.py +++ b/cpython-unix/build.py @@ -92,6 +92,14 @@ def add_target_env(env, build_platform, target_triple, build_env, build_options) extra_host_cflags = [] extra_host_ldflags = [] + if "debug" in build_options and "-linux-" in target_triple: + # Fortification requires optimization and is ineffective with debug's -O0. + extra_target_cflags = [ + flag + for flag in extra_target_cflags + if flag not in {"-U_FORTIFY_SOURCE", "-D_FORTIFY_SOURCE=3"} + ] + # Add compiler-rt for aarch64-musl to resolve missing builtins if target_triple == "aarch64-unknown-linux-musl": extra_target_cflags.append("--rtlib=compiler-rt") diff --git a/cpython-unix/targets.yml b/cpython-unix/targets.yml index d0db3e39e..391d3b700 100644 --- a/cpython-unix/targets.yml +++ b/cpython-unix/targets.yml @@ -130,6 +130,10 @@ aarch64-unknown-linux-gnu: target_cc: clang target_cxx: clang++ target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' - '-fvisibility=hidden' # Enable frame pointers - '-fno-omit-frame-pointer' @@ -139,6 +143,8 @@ aarch64-unknown-linux-gnu: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -185,9 +191,16 @@ armv7-unknown-linux-gnueabi: host_cxx: /usr/bin/x86_64-linux-gnu-g++ target_cc: /usr/bin/arm-linux-gnueabi-gcc target_cxx: /usr/bin/arm-linux-gnueabi-g++ + target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -229,9 +242,16 @@ armv7-unknown-linux-gnueabihf: host_cxx: /usr/bin/x86_64-linux-gnu-g++ target_cc: /usr/bin/arm-linux-gnueabihf-gcc target_cxx: /usr/bin/arm-linux-gnueabihf-g++ + target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -273,9 +293,16 @@ loongarch64-unknown-linux-gnu: host_cxx: /usr/bin/x86_64-linux-gnu-g++ target_cc: /usr/bin/loongarch64-linux-gnu-gcc target_cxx: /usr/bin/loongarch64-linux-gnu-g++ + target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' - '-Wl,-z,max-page-size=0x10000' needs: - autoconf @@ -318,9 +345,16 @@ mips-unknown-linux-gnu: host_cxx: /usr/bin/x86_64-linux-gnu-g++ target_cc: /usr/bin/mips-linux-gnu-gcc target_cxx: /usr/bin/mips-linux-gnu-g++ + target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -362,9 +396,16 @@ mipsel-unknown-linux-gnu: host_cxx: /usr/bin/x86_64-linux-gnu-g++ target_cc: /usr/bin/mipsel-linux-gnu-gcc target_cxx: /usr/bin/mipsel-linux-gnu-g++ + target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -406,9 +447,16 @@ ppc64le-unknown-linux-gnu: host_cxx: /usr/bin/x86_64-linux-gnu-g++ target_cc: /usr/bin/powerpc64le-linux-gnu-gcc target_cxx: /usr/bin/powerpc64le-linux-gnu-g++ + target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -450,9 +498,16 @@ riscv64-unknown-linux-gnu: host_cxx: /usr/bin/x86_64-linux-gnu-g++ target_cc: /usr/bin/riscv64-linux-gnu-gcc target_cxx: /usr/bin/riscv64-linux-gnu-g++ + target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -495,11 +550,17 @@ s390x-unknown-linux-gnu: target_cc: /usr/bin/s390x-linux-gnu-gcc target_cxx: /usr/bin/s390x-linux-gnu-g++ target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' # set the minimum compatibility level to z10 (released 2008) - '-march=z10' target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -598,6 +659,10 @@ x86_64-unknown-linux-gnu: target_cc: clang target_cxx: clang++ target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' - '-fvisibility=hidden' # Enable frame pointers - '-fno-omit-frame-pointer' @@ -607,6 +672,8 @@ x86_64-unknown-linux-gnu: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -650,6 +717,10 @@ x86_64_v2-unknown-linux-gnu: target_cc: clang target_cxx: clang++ target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' - '-march=x86-64-v2' - '-fvisibility=hidden' # Enable frame pointers @@ -660,6 +731,8 @@ x86_64_v2-unknown-linux-gnu: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -703,6 +776,10 @@ x86_64_v3-unknown-linux-gnu: target_cc: clang target_cxx: clang++ target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' - '-march=x86-64-v3' - '-fvisibility=hidden' # Enable frame pointers @@ -713,6 +790,8 @@ x86_64_v3-unknown-linux-gnu: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -756,6 +835,10 @@ x86_64_v4-unknown-linux-gnu: target_cc: clang target_cxx: clang++ target_cflags: + # Hardening + - '-fstack-protector-strong' + - '-U_FORTIFY_SOURCE' + - '-D_FORTIFY_SOURCE=3' - '-march=x86-64-v4' - '-fvisibility=hidden' # Enable frame pointers @@ -766,6 +849,8 @@ x86_64_v4-unknown-linux-gnu: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -809,6 +894,8 @@ x86_64-unknown-linux-musl: target_cc: musl-clang target_cxx: clang++ # TODO: Explore a musl-clang++ shim? target_cflags: + # Hardening + - '-fstack-protector-strong' - '-fvisibility=hidden' # Enable frame pointers - '-fno-omit-frame-pointer' @@ -816,6 +903,8 @@ x86_64-unknown-linux-musl: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -859,6 +948,8 @@ x86_64_v2-unknown-linux-musl: target_cc: musl-clang target_cxx: clang++ # TODO: Explore a musl-clang++ shim? target_cflags: + # Hardening + - '-fstack-protector-strong' - '-march=x86-64-v2' - '-fvisibility=hidden' # Enable frame pointers @@ -867,6 +958,8 @@ x86_64_v2-unknown-linux-musl: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -910,6 +1003,8 @@ x86_64_v3-unknown-linux-musl: target_cc: musl-clang target_cxx: clang++ # TODO: Explore a musl-clang++ shim? target_cflags: + # Hardening + - '-fstack-protector-strong' - '-march=x86-64-v3' - '-fvisibility=hidden' # Enable frame pointers @@ -918,6 +1013,8 @@ x86_64_v3-unknown-linux-musl: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -961,6 +1058,8 @@ x86_64_v4-unknown-linux-musl: target_cc: musl-clang target_cxx: clang++ # TODO: Explore a musl-clang++ shim? target_cflags: + # Hardening + - '-fstack-protector-strong' - '-march=x86-64-v4' - '-fvisibility=hidden' # Enable frame pointers @@ -969,6 +1068,8 @@ x86_64_v4-unknown-linux-musl: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb @@ -1016,6 +1117,8 @@ aarch64-unknown-linux-musl: target_cc: musl-clang target_cxx: clang++ target_cflags: + # Hardening + - '-fstack-protector-strong' - '-fvisibility=hidden' # Enable frame pointers - '-fno-omit-frame-pointer' @@ -1023,6 +1126,8 @@ aarch64-unknown-linux-musl: target_ldflags: # Hardening - '-Wl,-z,noexecstack' + - '-Wl,-z,relro' + - '-Wl,-z,now' needs: - autoconf - bdb From 973bf572a7a7c12e3dddfed13feeead083117a59 Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Thu, 25 Jun 2026 11:53:32 -0500 Subject: [PATCH 02/12] enable stack-clash protection on aarch64/x86-64 linux Enable stack-clash protection on aarch64 and x86-64 linux targets. Other architectures are cross-compiled and the toolchains may lack support for this functionality. --- cpython-unix/build-cpython.sh | 4 ++-- cpython-unix/targets.yml | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 0f61fe24a..5da0ca494 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -729,9 +729,9 @@ autoconf # 3.12 and earlier) CFLAGS_JIT="${CFLAGS}" -# JIT stencils cannot call the stack-protector symbols, so let CPython's -# -fno-stack-protector take precedence for their compilation. +# JIT stencils should not inherit target-wide stack hardening flags. CFLAGS_JIT="${CFLAGS_JIT//-fstack-protector-strong/}" +CFLAGS_JIT="${CFLAGS_JIT//-fstack-clash-protection/}" # In 3.14+, the JIT compiler on x86-64 Linux uses a model that conflicts with `-fPIC`, so strip it # from the flags. See: diff --git a/cpython-unix/targets.yml b/cpython-unix/targets.yml index 391d3b700..57cf57428 100644 --- a/cpython-unix/targets.yml +++ b/cpython-unix/targets.yml @@ -132,6 +132,7 @@ aarch64-unknown-linux-gnu: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' - '-fvisibility=hidden' @@ -661,6 +662,7 @@ x86_64-unknown-linux-gnu: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' - '-fvisibility=hidden' @@ -719,6 +721,7 @@ x86_64_v2-unknown-linux-gnu: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' - '-march=x86-64-v2' @@ -778,6 +781,7 @@ x86_64_v3-unknown-linux-gnu: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' - '-march=x86-64-v3' @@ -837,6 +841,7 @@ x86_64_v4-unknown-linux-gnu: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' - '-march=x86-64-v4' @@ -896,6 +901,7 @@ x86_64-unknown-linux-musl: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-fvisibility=hidden' # Enable frame pointers - '-fno-omit-frame-pointer' @@ -950,6 +956,7 @@ x86_64_v2-unknown-linux-musl: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-march=x86-64-v2' - '-fvisibility=hidden' # Enable frame pointers @@ -1005,6 +1012,7 @@ x86_64_v3-unknown-linux-musl: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-march=x86-64-v3' - '-fvisibility=hidden' # Enable frame pointers @@ -1060,6 +1068,7 @@ x86_64_v4-unknown-linux-musl: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-march=x86-64-v4' - '-fvisibility=hidden' # Enable frame pointers @@ -1119,6 +1128,7 @@ aarch64-unknown-linux-musl: target_cflags: # Hardening - '-fstack-protector-strong' + - '-fstack-clash-protection' - '-fvisibility=hidden' # Enable frame pointers - '-fno-omit-frame-pointer' From 4b6d8f056a4ed75c1ae944bb545031fd6d75a25d Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Thu, 25 Jun 2026 13:36:12 -0500 Subject: [PATCH 03/12] validate ELF hardening properties in distributions Extend validate-distribution to reject * non-PIE executables * writable and executable load segments * missing RELRO * lazy symbol binding * text relocations. Preserve the existing executable-stack validation and skip dynamic binding checks when validating static binaries. --- src/validation.rs | 81 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/src/validation.rs b/src/validation.rs index fb1824bc9..7232f91a1 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -10,8 +10,9 @@ use { object::{ Architecture, Endianness, FileKind, Object, SectionIndex, SymbolScope, elf::{ - ET_DYN, ET_EXEC, FileHeader32, FileHeader64, PF_X, PT_GNU_STACK, SHN_UNDEF, STB_GLOBAL, - STB_WEAK, STV_DEFAULT, STV_HIDDEN, + DF_1_NOW, DF_BIND_NOW, DF_TEXTREL, DT_BIND_NOW, DT_FLAGS, DT_FLAGS_1, DT_TEXTREL, + ET_DYN, ET_EXEC, FileHeader32, FileHeader64, PF_W, PF_X, PT_GNU_RELRO, PT_GNU_STACK, + PT_LOAD, SHN_UNDEF, STB_GLOBAL, STB_WEAK, STV_DEFAULT, STV_HIDDEN, }, macho::{LC_CODE_SIGNATURE, MH_OBJECT, MH_TWOLEVEL, MachHeader32, MachHeader64}, read::{ @@ -1027,9 +1028,14 @@ fn validate_elf>( let versions = sections.versions(endian, data)?; + let mut has_dynamic_section = false; + let mut has_bind_now = false; + let mut has_text_relocations = false; + for (section_index, section) in sections.iter().enumerate() { // Dynamic sections defined needed libraries, which we validate. if let Some((entries, index)) = section.dynamic(endian, data)? { + has_dynamic_section = true; let strings = sections.strings(endian, data, index).unwrap_or_default(); for entry in entries { @@ -1087,6 +1093,20 @@ fn validate_elf>( } } } + + match entry.tag32(endian) { + Some(DT_BIND_NOW) => has_bind_now = true, + Some(DT_FLAGS) => { + let flags = entry.val32(endian).unwrap_or_default(); + has_bind_now |= flags & DF_BIND_NOW != 0; + has_text_relocations |= flags & DF_TEXTREL != 0; + } + Some(DT_FLAGS_1) => { + has_bind_now |= entry.val32(endian).unwrap_or_default() & DF_1_NOW != 0; + } + Some(DT_TEXTREL) => has_text_relocations = true, + _ => {} + } } } @@ -1180,8 +1200,9 @@ fn validate_elf>( } } - // Verify that objects are not requesting an executable stack. For backwards compatibility, - // Linux (the kernel when loading an executable, and glibc when loading a shared library) + // Verify that objects are not requesting an executable stack and that other hardening + // properties exist in the linked outputs. For backwards compatibility, Linux (the kernel + // when loading an executable, and glibc when loading a shared library) // assumes you need an executable stack unless you request otherwise. In linked outputs // (executables and shared libraries) this is in the program header: the flags of a // PT_GNU_STACK entry specify stack permissions, and the default if unspecified is RWX. In @@ -1193,16 +1214,35 @@ fn validate_elf>( // .note.GNU-stack section, which we are overriding with -Wl,-z,noexecstack. if matches!(elf.e_type(endian), ET_EXEC | ET_DYN) { + if elf.e_type(endian) == ET_EXEC { + context.errors.push(format!( + "{} is not position-independent (ELF type is ET_EXEC)", + path.display(), + )); + } + let mut found_pt_gnu_stack = false; + let mut found_pt_gnu_relro = false; for phdr in elf.program_headers(endian, data)? { - if phdr.p_type(endian) != PT_GNU_STACK { - continue; - } - found_pt_gnu_stack = true; - if (phdr.p_flags(endian) & PF_X) != 0 { - context - .errors - .push(format!("{} requests executable stack", path.display())); + let flags = phdr.p_flags(endian); + + match phdr.p_type(endian) { + PT_GNU_STACK => { + found_pt_gnu_stack = true; + if flags & PF_X != 0 { + context + .errors + .push(format!("{} requests executable stack", path.display())); + } + } + PT_GNU_RELRO => found_pt_gnu_relro = true, + PT_LOAD if flags & (PF_W | PF_X) == (PF_W | PF_X) => { + context.errors.push(format!( + "{} has a writable and executable PT_LOAD segment", + path.display(), + )); + } + _ => {} } } if !found_pt_gnu_stack { @@ -1211,6 +1251,23 @@ fn validate_elf>( path.display(), )); } + if !found_pt_gnu_relro { + context.errors.push(format!( + "{} is missing a PT_GNU_RELRO segment", + path.display(), + )); + } + if has_dynamic_section && !has_bind_now { + context.errors.push(format!( + "{} does not request immediate symbol binding", + path.display(), + )); + } + if has_text_relocations { + context + .errors + .push(format!("{} contains text relocations", path.display())); + } } Ok(()) From 26601eb862280e14bd5ea645fe7cf43e8c30d459 Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Tue, 7 Jul 2026 13:12:58 -0500 Subject: [PATCH 04/12] do not disable PIE when applying BOLT --- cpython-unix/build-cpython.sh | 4 ++++ .../patch-configure-bolt-enable-pie.patch | 15 +++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 cpython-unix/patch-configure-bolt-enable-pie.patch diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 5da0ca494..666783006 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -227,6 +227,10 @@ if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then fi if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then + # CPython disables PIE for BOLT builds as a performance optimization, but + # modern BOLT supports position-independent executables. + patch -p1 -i "${ROOT}/patch-configure-bolt-enable-pie.patch" + # Additional BOLT optimizations, being upstreamed in # https://github.com/python/cpython/issues/128514 patch -p1 -i "${ROOT}/patch-configure-bolt-apply-flags-128514.patch" diff --git a/cpython-unix/patch-configure-bolt-enable-pie.patch b/cpython-unix/patch-configure-bolt-enable-pie.patch new file mode 100644 index 000000000..3754aca59 --- /dev/null +++ b/cpython-unix/patch-configure-bolt-enable-pie.patch @@ -0,0 +1,15 @@ +diff --git a/configure.ac b/configure.ac +index c5dc9bf174a..11631a978ce 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2142,11 +2142,7 @@ if test "$Py_BOLT" = 'true' ; then + # These flags are required for bolt to work: + LDFLAGS_NODIST="$LDFLAGS_NODIST -Wl,--emit-relocs" + +- # These flags are required to get good performance from bolt: +- CFLAGS_NODIST="$CFLAGS_NODIST -fno-pie" +- # We want to add these no-pie flags to linking executables but not shared libraries: +- LINKCC="$LINKCC -fno-pie -no-pie" + AC_SUBST([LLVM_BOLT]) + AC_PATH_TOOL([LLVM_BOLT], [llvm-bolt], [''], [${llvm_path}]) + if test -n "${LLVM_BOLT}" -a -x "${LLVM_BOLT}" From 8bf0998fe30446a37fefa2622f54bf01a3bb503f Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Wed, 8 Jul 2026 10:52:48 -0500 Subject: [PATCH 05/12] Revert "do not disable PIE when applying BOLT" This reverts commit 26601eb862280e14bd5ea645fe7cf43e8c30d459. --- cpython-unix/build-cpython.sh | 4 ---- .../patch-configure-bolt-enable-pie.patch | 15 --------------- 2 files changed, 19 deletions(-) delete mode 100644 cpython-unix/patch-configure-bolt-enable-pie.patch diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 666783006..5da0ca494 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -227,10 +227,6 @@ if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then fi if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then - # CPython disables PIE for BOLT builds as a performance optimization, but - # modern BOLT supports position-independent executables. - patch -p1 -i "${ROOT}/patch-configure-bolt-enable-pie.patch" - # Additional BOLT optimizations, being upstreamed in # https://github.com/python/cpython/issues/128514 patch -p1 -i "${ROOT}/patch-configure-bolt-apply-flags-128514.patch" diff --git a/cpython-unix/patch-configure-bolt-enable-pie.patch b/cpython-unix/patch-configure-bolt-enable-pie.patch deleted file mode 100644 index 3754aca59..000000000 --- a/cpython-unix/patch-configure-bolt-enable-pie.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/configure.ac b/configure.ac -index c5dc9bf174a..11631a978ce 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -2142,11 +2142,7 @@ if test "$Py_BOLT" = 'true' ; then - # These flags are required for bolt to work: - LDFLAGS_NODIST="$LDFLAGS_NODIST -Wl,--emit-relocs" - -- # These flags are required to get good performance from bolt: -- CFLAGS_NODIST="$CFLAGS_NODIST -fno-pie" -- # We want to add these no-pie flags to linking executables but not shared libraries: -- LINKCC="$LINKCC -fno-pie -no-pie" - AC_SUBST([LLVM_BOLT]) - AC_PATH_TOOL([LLVM_BOLT], [llvm-bolt], [''], [${llvm_path}]) - if test -n "${LLVM_BOLT}" -a -x "${LLVM_BOLT}" From 864daf291d6ac18413c166767fb8246f1e80e3f8 Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Wed, 8 Jul 2026 10:54:44 -0500 Subject: [PATCH 06/12] use -fstack-protector rather than ...-strong Apply the -fstack-protector flag rather that the strong -fstack-protector-strong. The strong version has a notiable effect on performance and is excluded in other builds within in ecosystem for performance reasons, including Debian's 3.13 builds in Trixie. --- cpython-unix/build-cpython.sh | 2 +- cpython-unix/targets.yml | 36 +++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 5da0ca494..2483cd2bd 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -730,7 +730,7 @@ autoconf CFLAGS_JIT="${CFLAGS}" # JIT stencils should not inherit target-wide stack hardening flags. -CFLAGS_JIT="${CFLAGS_JIT//-fstack-protector-strong/}" +CFLAGS_JIT="${CFLAGS_JIT//-fstack-protector/}" CFLAGS_JIT="${CFLAGS_JIT//-fstack-clash-protection/}" # In 3.14+, the JIT compiler on x86-64 Linux uses a model that conflicts with `-fPIC`, so strip it diff --git a/cpython-unix/targets.yml b/cpython-unix/targets.yml index 57cf57428..71d4d7c96 100644 --- a/cpython-unix/targets.yml +++ b/cpython-unix/targets.yml @@ -131,7 +131,7 @@ aarch64-unknown-linux-gnu: target_cxx: clang++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' @@ -194,7 +194,7 @@ armv7-unknown-linux-gnueabi: target_cxx: /usr/bin/arm-linux-gnueabi-g++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' target_ldflags: @@ -245,7 +245,7 @@ armv7-unknown-linux-gnueabihf: target_cxx: /usr/bin/arm-linux-gnueabihf-g++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' target_ldflags: @@ -296,7 +296,7 @@ loongarch64-unknown-linux-gnu: target_cxx: /usr/bin/loongarch64-linux-gnu-g++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' target_ldflags: @@ -348,7 +348,7 @@ mips-unknown-linux-gnu: target_cxx: /usr/bin/mips-linux-gnu-g++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' target_ldflags: @@ -399,7 +399,7 @@ mipsel-unknown-linux-gnu: target_cxx: /usr/bin/mipsel-linux-gnu-g++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' target_ldflags: @@ -450,7 +450,7 @@ ppc64le-unknown-linux-gnu: target_cxx: /usr/bin/powerpc64le-linux-gnu-g++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' target_ldflags: @@ -501,7 +501,7 @@ riscv64-unknown-linux-gnu: target_cxx: /usr/bin/riscv64-linux-gnu-g++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' target_ldflags: @@ -552,7 +552,7 @@ s390x-unknown-linux-gnu: target_cxx: /usr/bin/s390x-linux-gnu-g++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' # set the minimum compatibility level to z10 (released 2008) @@ -661,7 +661,7 @@ x86_64-unknown-linux-gnu: target_cxx: clang++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' @@ -720,7 +720,7 @@ x86_64_v2-unknown-linux-gnu: target_cxx: clang++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' @@ -780,7 +780,7 @@ x86_64_v3-unknown-linux-gnu: target_cxx: clang++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' @@ -840,7 +840,7 @@ x86_64_v4-unknown-linux-gnu: target_cxx: clang++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - '-D_FORTIFY_SOURCE=3' @@ -900,7 +900,7 @@ x86_64-unknown-linux-musl: target_cxx: clang++ # TODO: Explore a musl-clang++ shim? target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-fvisibility=hidden' # Enable frame pointers @@ -955,7 +955,7 @@ x86_64_v2-unknown-linux-musl: target_cxx: clang++ # TODO: Explore a musl-clang++ shim? target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-march=x86-64-v2' - '-fvisibility=hidden' @@ -1011,7 +1011,7 @@ x86_64_v3-unknown-linux-musl: target_cxx: clang++ # TODO: Explore a musl-clang++ shim? target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-march=x86-64-v3' - '-fvisibility=hidden' @@ -1067,7 +1067,7 @@ x86_64_v4-unknown-linux-musl: target_cxx: clang++ # TODO: Explore a musl-clang++ shim? target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-march=x86-64-v4' - '-fvisibility=hidden' @@ -1127,7 +1127,7 @@ aarch64-unknown-linux-musl: target_cxx: clang++ target_cflags: # Hardening - - '-fstack-protector-strong' + - '-fstack-protector' - '-fstack-clash-protection' - '-fvisibility=hidden' # Enable frame pointers From 5e84e8b34e8b9bafc0c8f288c8e7ffa6ca7f8f8d Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Wed, 8 Jul 2026 11:07:42 -0500 Subject: [PATCH 07/12] do not require PIE --- src/validation.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/validation.rs b/src/validation.rs index 7232f91a1..4e0ecf137 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -1214,13 +1214,6 @@ fn validate_elf>( // .note.GNU-stack section, which we are overriding with -Wl,-z,noexecstack. if matches!(elf.e_type(endian), ET_EXEC | ET_DYN) { - if elf.e_type(endian) == ET_EXEC { - context.errors.push(format!( - "{} is not position-independent (ELF type is ET_EXEC)", - path.display(), - )); - } - let mut found_pt_gnu_stack = false; let mut found_pt_gnu_relro = false; for phdr in elf.program_headers(endian, data)? { From 5b9fb1a31e2e5c77c4c71cca5744b417f3d4c06e Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Wed, 8 Jul 2026 11:13:47 -0500 Subject: [PATCH 08/12] add ld-linux-aarch64.so.1 to allowed libraries ld-linux-aarch64.so.1 is an allowed library on aarch64-linux. Hardening flags can promote this library to DT_NEEDED where as before it was only DT_INTERP. --- src/validation.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/validation.rs b/src/validation.rs index 4e0ecf137..f0bbc71b6 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -259,7 +259,10 @@ static ELF_ALLOWED_LIBRARIES_BY_TRIPLE: Lazy Date: Wed, 8 Jul 2026 13:45:55 -0500 Subject: [PATCH 09/12] explicitly pass -pie on ppc64le to work around gcc bug --- cpython-unix/build-cpython.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 2483cd2bd..481b70fbc 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -747,6 +747,14 @@ CFLAGS=$CFLAGS CPPFLAGS=$CFLAGS CFLAGS_JIT=$CFLAGS_JIT LDFLAGS=$LDFLAGS \ # Supplement produced Makefile with our modifications. cat ../Makefile.extra >> Makefile +# Debian's PPC64LE GCC 6 defaults to PIE but selects the non-PIE startup object unless -pie is +# passed explicitly. Add it only to LINKFORSHARED, which CPython uses when linking executables. +# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81170. +# TODO(jjh)Remove when the ppc64le toolchain changes +if [[ "${TARGET_TRIPLE}" = "ppc64le-unknown-linux-gnu" ]]; then + printf '\nLINKFORSHARED += -pie\n' >> Makefile +fi + make -j "${NUM_CPUS}" make -j "${NUM_CPUS}" sharedinstall DESTDIR="${ROOT}/out/python" make -j "${NUM_CPUS}" install DESTDIR="${ROOT}/out/python" From b7306d8927a25c6da378684412aa4b3f1d1b559d Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Thu, 9 Jul 2026 15:33:36 -0500 Subject: [PATCH 10/12] use D_FORTIFY_SOURCE=2 rather than 3 --- cpython-unix/build.py | 2 +- cpython-unix/targets.yml | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cpython-unix/build.py b/cpython-unix/build.py index caba94e85..e50668028 100755 --- a/cpython-unix/build.py +++ b/cpython-unix/build.py @@ -97,7 +97,7 @@ def add_target_env(env, build_platform, target_triple, build_env, build_options) extra_target_cflags = [ flag for flag in extra_target_cflags - if flag not in {"-U_FORTIFY_SOURCE", "-D_FORTIFY_SOURCE=3"} + if flag not in {"-U_FORTIFY_SOURCE", "-D_FORTIFY_SOURCE=2"} ] # Add compiler-rt for aarch64-musl to resolve missing builtins diff --git a/cpython-unix/targets.yml b/cpython-unix/targets.yml index 71d4d7c96..88f3f94c8 100644 --- a/cpython-unix/targets.yml +++ b/cpython-unix/targets.yml @@ -134,7 +134,7 @@ aarch64-unknown-linux-gnu: - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' - '-fvisibility=hidden' # Enable frame pointers - '-fno-omit-frame-pointer' @@ -196,7 +196,7 @@ armv7-unknown-linux-gnueabi: # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' target_ldflags: # Hardening - '-Wl,-z,noexecstack' @@ -247,7 +247,7 @@ armv7-unknown-linux-gnueabihf: # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' target_ldflags: # Hardening - '-Wl,-z,noexecstack' @@ -298,7 +298,7 @@ loongarch64-unknown-linux-gnu: # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' target_ldflags: # Hardening - '-Wl,-z,noexecstack' @@ -350,7 +350,7 @@ mips-unknown-linux-gnu: # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' target_ldflags: # Hardening - '-Wl,-z,noexecstack' @@ -401,7 +401,7 @@ mipsel-unknown-linux-gnu: # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' target_ldflags: # Hardening - '-Wl,-z,noexecstack' @@ -452,7 +452,7 @@ ppc64le-unknown-linux-gnu: # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' target_ldflags: # Hardening - '-Wl,-z,noexecstack' @@ -503,7 +503,7 @@ riscv64-unknown-linux-gnu: # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' target_ldflags: # Hardening - '-Wl,-z,noexecstack' @@ -554,7 +554,7 @@ s390x-unknown-linux-gnu: # Hardening - '-fstack-protector' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' # set the minimum compatibility level to z10 (released 2008) - '-march=z10' target_ldflags: @@ -664,7 +664,7 @@ x86_64-unknown-linux-gnu: - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' - '-fvisibility=hidden' # Enable frame pointers - '-fno-omit-frame-pointer' @@ -723,7 +723,7 @@ x86_64_v2-unknown-linux-gnu: - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' - '-march=x86-64-v2' - '-fvisibility=hidden' # Enable frame pointers @@ -783,7 +783,7 @@ x86_64_v3-unknown-linux-gnu: - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' - '-march=x86-64-v3' - '-fvisibility=hidden' # Enable frame pointers @@ -843,7 +843,7 @@ x86_64_v4-unknown-linux-gnu: - '-fstack-protector' - '-fstack-clash-protection' - '-U_FORTIFY_SOURCE' - - '-D_FORTIFY_SOURCE=3' + - '-D_FORTIFY_SOURCE=2' - '-march=x86-64-v4' - '-fvisibility=hidden' # Enable frame pointers From fd148fe73099e3fdb42268503c9afaa49ac6bb3e Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Thu, 9 Jul 2026 15:41:35 -0500 Subject: [PATCH 11/12] append U_FORTIFY_SOURCE in debug builds rather than removing --- cpython-unix/build.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/cpython-unix/build.py b/cpython-unix/build.py index e50668028..d8bf04308 100755 --- a/cpython-unix/build.py +++ b/cpython-unix/build.py @@ -92,14 +92,6 @@ def add_target_env(env, build_platform, target_triple, build_env, build_options) extra_host_cflags = [] extra_host_ldflags = [] - if "debug" in build_options and "-linux-" in target_triple: - # Fortification requires optimization and is ineffective with debug's -O0. - extra_target_cflags = [ - flag - for flag in extra_target_cflags - if flag not in {"-U_FORTIFY_SOURCE", "-D_FORTIFY_SOURCE=2"} - ] - # Add compiler-rt for aarch64-musl to resolve missing builtins if target_triple == "aarch64-unknown-linux-musl": extra_target_cflags.append("--rtlib=compiler-rt") @@ -216,6 +208,11 @@ def add_target_env(env, build_platform, target_triple, build_env, build_options) else: raise Exception("unhandled build platform: %s" % build_platform) + if "debug" in build_options and "-linux-" in target_triple: + # Fortification requires optimization and is ineffective with debug's -O0. Keep this last + # so it overrides any configured fortification level. + extra_target_cflags.append("-U_FORTIFY_SOURCE") + env["EXTRA_HOST_CFLAGS"] = " ".join(extra_host_cflags) env["EXTRA_HOST_LDFLAGS"] = " ".join(extra_host_ldflags) env["EXTRA_TARGET_CFLAGS"] = " ".join(extra_target_cflags) From 3d6ee528f208c34d96a071256e7fd0257ad5d643 Mon Sep 17 00:00:00 2001 From: "Jonathan J. Helmus" Date: Thu, 9 Jul 2026 15:49:00 -0500 Subject: [PATCH 12/12] override rather than remove stack hardenig flags --- cpython-unix/build-cpython.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpython-unix/build-cpython.sh b/cpython-unix/build-cpython.sh index 481b70fbc..381521282 100755 --- a/cpython-unix/build-cpython.sh +++ b/cpython-unix/build-cpython.sh @@ -730,8 +730,7 @@ autoconf CFLAGS_JIT="${CFLAGS}" # JIT stencils should not inherit target-wide stack hardening flags. -CFLAGS_JIT="${CFLAGS_JIT//-fstack-protector/}" -CFLAGS_JIT="${CFLAGS_JIT//-fstack-clash-protection/}" +CFLAGS_JIT+=" -fno-stack-protector -fno-stack-clash-protection" # In 3.14+, the JIT compiler on x86-64 Linux uses a model that conflicts with `-fPIC`, so strip it # from the flags. See: