Building extensions with hardening flags (via manylinux)

Posting here at the request of @mayeut to get a broader audience.

The Fedora project aims to enable user-level support for x86_64 shadow stacks in the Fedora Linux 46 release, which is scheduled for spring 2027. The effort is being coordinated by @submachine; technical details can be found in the Fedora change proposal.

This will affect Python in the following way.

A multi-threaded process that runs with the shadow-stack feature on will refuse to dlopen() a shared library that was built without the -fcf-protection compiler option. This may (or will?) break Python extensions that are shipped in binary form.

I have been pointed to PEP 600 and the manylinux project, which defines how re-distributable binary Python extensions are built. In my understanding, what needs to be done is roughly the following:

  1. Add -fcf-protection to manylinux’ hardening build flags.
  2. Make sure the hardening flags end up in the sysconfig module (by passing them to ./configure as e.g. CFLAGS rather than CFLAGS_NODIST).

There is a GitHub pull request with a first draft of what the implementation could look like.

First impression: this looks quite breaking, and not only for already-released wheels on PyPI (which are non-fixable of course, so that alone might be a showstopper). A couple of comments:

  • Your point (2) doesn’t do what you think it does. sysconfig.get_config_var('CFLAGS') may be used by setuptools, but isn’t read by any other build backend - only a CFLAGS already set as an environment variable will have an effect.
    • Even if you could convince manylinux to export CFLAGS, that can be easily and silently overridden by an export CFLAGS in the build scripts for a package (e.g., in cibw_before_build.sh)
    • You have to worry about other languages beyond C/C++ too (Rust, Fortran, Zig, etc.).
  • There’s more than just extension modules. For example:
    • Regular shared libraries repackaged into wheels and shipped on PyPI. Concrete example: CUDA libraries like nvidia-cublas · PyPI, which is explicitly dlopen’d by PyTorch et al.
    • Other ways of installing Python itself, e.g. through uv (python-build-standalone) or pixi (from conda-forge); that may or may not yield a Shadow Stack-enabled python binary.
  • There are vendored shared libraries in wheels (pulled in via auditwheel), they get built in all sorts of ad-hoc ways, or grabbed from other distros (Ubuntu, Conda, etc.).
  • What about hand-written assembly[1]? I’m leaving my comfort zone there, but a bit of help from Claude suggests it needs either #include <cet.h> or forcing -Wl,-z,shstk.

I think these are the critical sentences in the doc you linked:

Processes that load any non-compliant shared object will have Shadow Stack silently disabled at startup.

The only exception is applications that are fully Shadow Stack compliant at startup and then use dlopen to load a shared object without Shadow Stack markup at runtime.

Is only python checked at startup, and anything pulled in via an import statement isn’t? So all extension modules and everything they depend on with DT_NEEDED (not just what they explicitly dlopen) is a potential problem?

Last question: will the glibc.cpu.x86_shstk default be permissive or on? That seems important for the impact, and I don’t think the doc says anything, only that one can opt out by changing the setting to off.


  1. Not uncommon, OpenBLAS, OpenSSL, libjpeg-turbo, ffmpeg, etc. ↩︎

I’m going to speak on the side of the Fedora Project. Please note that this is not the Fedora Project’s opinion, as I’m merely a community member

I’m sure @sicherha expected these changes to be at least somewhat breaking. This was discussed in some aspects on Fedora’s discussion mediums (like the devel mailing list, and the Fedora Project discussion forums, for instance).

(note that I did throw out some criticisms against the GCC developers on the ML, they don’t matter for this discussion, so please ignore them :slight_smile:, also, I believe the GCC developers do a fine job for the most part)


I believe it is more important to note that this is an objective that some within the Fedora Project wish to meet eventually. CET features help to increase security by preventing potential hackers from taking part in stack shenanigans, which may involve rewriting various call-stack pointers to suit their purposes.

At the moment, the Fedora Project plans on switching the feature to permissive, rather than on, so this isn’t something that CPython/PyPI should be concerned with immediately. However, it is likely that down the line, Fedora will want to switch the feature to on. I suggest to start a dialog with the developers on PyPI regarding this issue as soon as possible, so that everything may be accounted for in the future.

I’m curious to know what the current status of implementing stack canaries are, in regards to PyPI packages? Was there ever a call made to do so, or do some packages implement them, while others do not?