AArch64: GCS lock implementation options

Yury Khrustalev yury.khrustalev@arm.com
Mon Jan 26 11:35:18 GMT 2026


Hi all,

A request for feedback about proposed solutions for this problem.

We need a way to implement GCS (shadow stack) [1] locking for cases
when GCS is enabled via the glibc.cpu.aarch64_gcs tunable.

This feature is not trivial because GCS may support several operations,
and each of them can be locked separately. By default, we want to lock
all possible operations but ideally we want to allow not locking some
of them, and we'd like that to be a runtime choice (so that one would
not have to rebuild Glibc to try a different set of locked operations
on shadow stack).

Currently, there are 3 operations that are supported [1]:

 - enable / disable
 - arbitrary write to shadow stack
 - push to shadow stack

Each of these 3 operations, if unlocked, would undermine the security
guarantees provided by GCS, so naturally, if we enabled GCS, we want
these 3 operations to be locked. When locked, an attempt to perform
an operation will be an error. For example, if enable / disable is
locked after GCS is enabled, an attempt to disable GCS via the prctl
syscall, it will be an error and the GCS will remain enabled.

The implementations of prctl(PR_SET_SHADOW_STACK_STATUS, ...) allows
to lock any bit including any currently unused bits. This is useful
because we can lock all operations in a compatible way.

The proposed solution is that by default all possible operations are
locked if GCS is enabled.

However, it is possible that more operations on shadow stacks will be
added in the future. They may be useful, for example, for tools like
debuggers or callstack analysers. In this case it may be desired to
keep GCS enabled while allow some of the operations on shadow stack.

This is why we need a runtime option to allow opt-out of locking of
certain operations (except for the 3 operations listed above, these
must be locked unconditionally).

Original proposal to just allow to opt out from locking via a bool
tunable [2] was rejected, understandably.

Instead, we could proceed in one of the following ways:

1) Lock everything unconditionally (this is the simplest option but
   it may prove to be impractical for reasons outlined above).

2) Add new tunable to provide opt-out bitmask. In this mask the 3 LSB
   will be ignored (they correspond to the 3 operations listed above
   that are always locked). The remaining bits, if set, will designate
   which operations should not be locked when GCS is enabled.

3) We could extend the existing tunable glibc.cpu.aarch64_gcs which
   is a 64 bit value and only 2 bits are used for GCS modes. This could
   be a problem for existing Glibc implementations that would fail to
   understand new values, although we could backport a fix for that.
   The only advantage of this solution is that we don't have more
   tunables.

I'd like to request feedback about these proposed solutions. Thanks!

Kind regards,
Yury

---

[1]: https://docs.kernel.org/arch/arm64/gcs.html
[2]: https://inbox.sourceware.org/libc-alpha/20251212154659.142848-1-yury.khrustalev@arm.com



More information about the Libc-help mailing list