F45 Change Proposal: Enable Shadow Stack by Default on x86_64 (system-wide)

Enable Shadow Stack by Default on x86_64

This is a proposed Change for Fedora Linux.
This document represents a proposed Change. As part of the Changes process, proposals are publicly announced in order to receive community feedback. This proposal will only be implemented if approved by the Fedora Engineering Steering Committee.

Wiki
Announced

:link: Summary

This change enables Shadow Stack protection on applications and libraries built with gcc (C, C++), clang (C, C++), and rustc (Rust) by default on x86_64 machines that support it on Fedora Linux 45. The dynamic linker or static startup routines will activate Shadow Stack for any process whose binary and shared library dependencies are all built with Shadow Stack support (marked with ELF metadata), protecting processes by default whenever possible.

:link: Owner

:link: Current status

  • Targeted release: Fedora Linux 45
  • Last updated: 2026-06-30
  • [ Announced]
  • [ Discussion thread]
  • FESCo issue:
  • Tracker bug:
  • Release notes tracker:

:link: Detailed Description

Shadow Stacks are a hardware enforced security feature that maintains a separate, tamper proof list of return addresses to protect against Return-Oriented Programming style exploits. This change enables Shadow Stack protection by default on x86_64 machines that support it on Fedora Linux 45. The dynamic linker, or static startup routines, will activate Shadow Stack for any process whose binary and shared library dependencies are all built with Shadow Stack support, protecting processes by default whenever possible. Shadow Stacks are one of two Control-Flow Enforcement features introduced in Intel CET, alongside Indirect Branch Tracking (IBT), designed to defend against Return-Oriented Programming (ROP) and Jump-Oriented Programming (JOP) attacks by protecting return addresses. This Fedora change only covers enabling Shadow Stack support. Enabling Indirect Branch Tracking by default is not in scope.

This change is backward compatible for the most part: -fcf-protection is a default compile time flag already enabled in redhat-rpm-config for Fedora since 2018 and thus the majority of binaries are already built with the appropriate markup. Thus, after this change is applied, applications whose dependencies carry Shadow Stack markup gain protection transparently while applications that load any non-compliant object at startup continue to run without Shadow Stack protection. The only new failure mode is when a Shadow Stack enabled process attempts to dlopen a non-compliant shared object at runtime, which results in a dlopen error that looks like error: dlopen: /path/to/library.so: rebuild shared object with SHSTK support enabled.

Two mitigation strategies can be used, depending on the package:

  1. Where the dlopened library can be fixed to include Shadow Stack support, the library is fixed and rebuilt with the appropriate markup. This is the preferred fix because it preserves Shadow Stack protection for the calling application.
  2. Where the non-compliant code cannot be fixed in time, the calling application is opted out of Shadow Stack via glibc’s System-Wide Tunables configuration that can be shipped per-application as a configuration file. This disables Shadow Stack for that specific application while all other processes retain protection.

:link: Feedback

No community feedback has been received yet. This section will be updated as feedback is collected.

:link: Benefit to Fedora

This change provides hardware-enforced protection against Return-Oriented Programming (ROP) style attacks for the majority of Fedora binaries at negligible performance cost.

Shadow Stacks are enforced by the CPU and do not require software instrumentation at runtime. The overhead is limited to maintaining a second copy of the return address stack in protected memory, which has no measurable impact on typical workloads.

The change is transparent: compliant applications are protected without any code change, rebuild, or user action. Because -fcf-protection is already a default compile flag in Fedora, the majority of packages are already built with the appropriate ELF markup. Enabling Shadow Stack in the dynamic linker activates protection for these packages immediately.

This also lays groundwork for enabling Indirect Branch Tracking (IBT) in a future release, which together with Shadow Stack would provide full Control-Flow Enforcement Technology (CET) protection.

:link: Scope

  • Proposal owners: The glibc maintainers will:
  1. Apply a glibc downstream patch that reverts an upstream change that deactivates Shadow Stack support.
  2. Include the upstream glibc System-Wide Tunables feature, which has obtained upstream consensus and will be part of the next glibc release. This feature enables per-application opt-out via configuration files.
  3. Ship /etc/tunables.conf with include /etc/tunables.conf.d/*.conf so that individual packages can drop in their own configuration files in order to opt-out.
  4. Validate that SUID/SGID applications correctly sanitize tunables so that unprivileged users cannot disable Shadow Stack for privileged programs.
  5. Open tracking bugs for all known incompatible packages.
  • Other developers:

Most packages built with Fedora’s default flags already produce Shadow Stack compatible binaries and no action is needed in this case. Packages that include hand-written assembly or build with different flags might ship ELF objects without Shadow Stack markup. Maintainers of such packages will need to make changes to their package to include Shadow Stack markup (e.g., annotate assembly routines).

A special case is applications built with Shadow Stack support that currently dlopen shared objects built without it. These applications will encounter a runtime error at dlopen unless one of these fixes is applied:

  1. The dlopened library is compiled with Shadow Stack support enabled.
  2. The application opts out of Shadow Stack support using glibc’s System-Wide Tunables feature. For example, a package named app providing /usr/bin/app would install /etc/tunables.conf.d/app.conf containing:

[proc:/usr/bin/app] glibc.cpu.x86_shstk=off

This disables Shadow Stack for that specific application while all other processes retain protection. The drop-in file is owned by the affected package’s RPM, so it is added and removed with the package. Once the dependency is fixed, the package update removes the drop-in file and the application gains Shadow Stack protection.

Most objects are already built with support, and enablement is via a glibc update. For Rust, binary packages need to be rebuilt once the Rust dependency is in place. This can be done as part of the regularly scheduled Fedora 45 mass-rebuild. We will track this as part of the F45 GNU Toolchain release engineering ticket linked to above.

  • Policies and guidelines:

No changes to packaging guidelines are required at this time. Default build configuration already builds objects with support and for the majority of packages, the change is transparent.

  • Trademark approval: N/A (not needed for this Change)

  • Alignment with the Fedora Strategy:

This change is in line with Fedora’s security posture and commitment to shipping secure-by-default systems.

:link: Upgrade/compatibility impact

After upgrading to Fedora 45, Shadow Stack support will be enabled automatically for processes whose binary and all loaded shared libraries carry the appropriate markup. 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. This will lead to a dlopen failure. However, affected applications can be configured to opt out of Shadow Stack protection, or have their dependencies fixed. Incompatible packages will be identified by scanning the Fedora package set for ELF objects missing the SHSTK property note, and by testing critical desktop and server workloads against the COPR repository with Shadow Stack enabled. Tracking bugs will be filed for all known incompatible packages. This work has already started and is being tracked in Bugzilla.

  1. Where the dlopened library is under Fedora’s control, it can either be fixed and rebuilt with support, or the dependent package configured to run with Shadow Stack turned off as described in the Scope section above.
  2. Users who encounter this problem with third party applications can work around it using the System-Wide Tunables feature as described earlier.

:link: Early Testing (Optional)

A COPR repository with the updated glibc (Shadow Stack enabled) is available for Fedora 43, 44, and Rawhide for early testing: Making sure you're not a bot!

Do you require ‘QA Blueprint’ support? No

:link: How To Test

  1. Shadow Stack hardware support is available on Intel 11th Generation and newer CPUs, and AMD Zen3 and newer CPUs, so testing does not require any special hardware except a recent enough x86_64 processor.

  2. Machine learning workloads that use GPU acceleration via AMD ROCm are a specific area of interest because the ROCm runtime libraries may lack Shadow Stack markup. Testing these workloads (e.g., pytorch with ROCm) requires access to AMD GPUs.

  3. Once the runtime has been enabled for Shadow Stack, testing involves (a) verifying the shadow stack status of running processes (cat /proc/$pid/status | grep x86_Thread_features), and (b) verifying that there are no application errors or crashes due to incompatible binaries.

  4. The majority of running processes should be protected, with Shadow Stack enabled.

:link: User Experience

This change is transparent to users. No action is required to enable or configure support. Applications that are not compatible with Shadow Stack run without the protection; they continue to function normally but do not benefit from the additional security. In rare cases, an application that loads a non-compliant plugin at runtime may encounter a dlopen error. Known affected applications ship with a drop-in configuration file that prevents this. Users who encounter this with other applications can create a file in /etc/tunables.conf.d/ to opt the application out, and then run ldconfig.

:link: Dependencies

  1. Rust: The rust compiler needs to be able to generate Shadow Stack compatible binaries. This affects a significant part of the Fedora runtime and applications, including rpm and firefox. However, we are collaborating with the Rust team and have already tested a rustc change that enables emitting compatible binaries by default (without the use of any flags). The Rust Fedora Bug tracks this.

  2. Firefox and Chrome: Both browsers are currently not compatible with Shadow Stack although the Firefox binary and some objects are built with Shadow Stack enabled. Upstream work, in particular around their JIT compilers, as well as the rust tooling change mentioned above are needed in order to fix this. Until then, we propose to disable Shadow Stack protection flags when building Firefox. Both browsers will gain Shadow Stack protection once their upstream dependencies are resolved, at which point they can be rebuilt with protection turned on.

:link: Contingency Plan

  • Contingency mechanism: Downstream patch applied to glibc to enable Shadow Stacks by default in Fedora will be reverted.

  • Contingency deadline: No deadline. The patch can be reverted as late as necessary in the development cycle.

  • Blocks release? No.

:link: Documentation

:link: Guide for package maintainers

:link: Checking an ELF object for CET support

Use readelf to inspect the GNU property note on any object file, shared library, or binary. e.g.:

readelf -n /usr/lib64/libc.so.6 | grep ‘IBT|SHSTK’

A fully compliant object shows both properties:

Properties: x86 feature: IBT, SHSTK

While this change only requires SHSTK, IBT (Indirect Branch Tracking) will be enabled in a future release and fixing both now avoids having to revisit these packages later. For this change, only SHSTK is required. An object with IBT but not SHSTK will still cause Shadow Stack to be disabled for the process. An object with SHSTK but not IBT is fully compliant with this change. Fixing both properties is recommended so that packages are ready for a future IBT enablement, but only SHSTK is needed now. The linker ANDs these bits across all inputs, so a single unmarked object will strip the property from the final binary. Packages are built by default with -fcf-protection enabled, so source files when compiled with default flags are automatically built with SHSTK/IBT support. However, hand-written assembly needs annotations in order to be built with the property enabled. This leads to situations where linking against objects built with hand-written assembly without the appropriate annotations strips the final binary or shared object of CET support.

:link: Annotating a GAS assembly file

Hand-written .S files compiled through GCC do not automatically receive the CET property note. To fix this, add one line near the top of the file:

#include <cet.h>

For IBT compatibility, add _CET_ENDBR (also defined by cet.h) at the entry point of any function that may be reached via an indirect branch (e.g. function pointer):

#include <cet.h> .text .globl my_handwritten_func .type my_handwritten_func, @function my_handwritten_func: _CET_ENDBR /* ..function body */ ret

:link: Release Notes

Last edited by @amoloney 2026-07-01T16:12:28Z

Last edited by @amoloney 2026-07-01T16:12:28Z

How do you feel about the proposal as written?

  • Strongly in favor
  • In favor, with reservations
  • Neutral
  • Opposed, but could be convinced
  • Strongly opposed
0 voters

If you are in favor but have reservations, or are opposed but something could change your mind, please explain in a reply.

We want everyone to be heard, but many posts repeating the same thing actually makes that harder. If you have something new to say, please say it. If, instead, you find someone has already covered what you’d like to express, please simply give that post a :heart: instead of reiterating. You can even do this by email, by replying with the heart emoji or just “+1”. This will make long topics easier to follow.

Please note that this is an advisory “straw poll” meant to gauge sentiment. It isn’t a vote or a scientific survey. See About the Change Proposals category for more about the Change Process and moderation policy.

From an OpenSSL perspective, it looks suspicious to me. We have a lot of hand-written assembly. Additionally, OpenSSL provider model would require building 3rd-party providers with Shadow Stack protection, which will break backward compatibility (currently providers built for OpenSSL 3 work with OpenSSL 4).

Unless that hand-written assembly code does funny things with the return address on the call stack - e.g. coroutine switching or trampolines - I would expect it to be 100 % compatible with the Shadow Stack feature. Deeper scrutiny of assembly code will hopefully only become necessary if and when Indirect-Branch Tracking is on the agenda - but that lies outside the scope of the proposal at hand.

The issue with third-party providers is potentially somewhat concerning. One last-resort fall-back option to address it might be to strip the SHSTK note from the OpenSSL library’s ELF header. This would prevent the Shadow Stack from being enabled for any program that links against OpenSSL. Not pretty at all, but it would allow the distribution as a whole to move forward now and subsequently address the remaining issues with individual packages one by one.

I, for one, strongly support the move to finally make Linux user space benefit from the security features provided by modern CPUs.

1 Like

To throw a wrench into the process, systemd now loads libcrypto.so using dlopen(3). So the solution with stripping SHSTK from the library will not work, it’d need to be done recursively in systemd too. (Not systemd the program, but a bunch of systemd programs, systemd-resolved, systemd-sbsign, etc.)

1 Like

I’m not convinced OpenSSL is the problem here. First, how many 3rd party providers actually are there? At the moment I’m only aware of oqsprovider, Intel’s QAT, and the pkcs11-provider, all of which are AFAIK packaged in Fedora, i.e., they aren’t affected.

This would only be a problem for 3rd party binaries that would stop working on a newer Fedora — on the other hand, there was never a guarantee that a binary compiled for one Fedora version works unmodified on the next one.

What I think might actually be a serious issue, though, is that this would likely break most users of Python virtualenvs that install packages using pip — a common dependency is python cryptography, which often gets installed as a wheel in prebuilt against the ABI specs managed by the manylinux project. These are then dlopen()ed when the corresponding module is imported, so unless the Python interpreter (and everything that embeds a Python interpreter) is marked as exempt, this will break many developers’ workflows.

There are plenty of 3rd-party providers that are not packaged for Fedora, we just don’t know about them. E.g. aurora (Rust-based) by Nicola Tuveri.

And yes, in a talk with Simo he mentioned that Rust may be also a problem (and I suspect Go and Java).

Aurora is a research project as far as I know? I’m sure Nicola can recompile it with the appropriate flags (unless that’s a major hassle in a Rust toolchain, which could be a thing I’m not aware of).

For those who want to dig deeper, can you elaborate as to why this was reverted in glibc upstream? Preferably with pointers to discussions there

(I am assuming it has to do with the opt out mechanism not being available initially)