ARM64: GCS: RET history and GCSSS1 stack switching
Igor Svilenkov Bozic
svilenkov at gmail.com
Wed Sep 17 03:08:48 PDT 2025
Hi all,
I've recently completed work on Guarded Control Stack (GCS) support
for Arm64 in CRIU, as part of Google Summer of Code 2025 under
mentorship from the CRIU maintainers. The effort focused on enabling
userland support for GCS-based checkpoint/restore, including
experiments with stack migration, token management, and testing on the
FVP Base RevC platform.
While studying the architecture and running tests (mainly from
tools/testing/selftests/arm64/gcs/), I had a couple of questions about
the GCS behavior:
1. Retired entries lingering after RETs:
When using a GCS-aware program that pushes return addresses and then
RETs, I noticed that the slots below the GCSPR_EL0 pointer still
contain "retired entries" from the previous call frame(s). Is this
leftover prior state expected as a feature, perhaps to assist
profiling/debug tools by preserving a history of RETs?
If so, is there a stricter mode to force a wipe of the shadow stack
after a RET, in scenarios where maximum security is preferred?
2. GCSSS1 usage with new stacks
When switching to a freshly mapped GCS shadow stack, it appears that
GCSSS1 requires jumping to an address that already contains a valid
(completed) token, which is then consumed and replaced with an
in-progress token.
As a result, it's not possible to do a RET to a return address higher
in the call stack prior to the GCSSS1 call site. For example, if the
shadow stack switch occurs in a call chain like main() →
subfunction(), you cannot directly RET back up to main() while the new
stack is active. Instead, either the new stack must be manipulated
(given that it's a writable GCS) by copying (via GCSSTR) to the newly
switched shadow stack or by fabricating a sigframe with a new
GCSPR_EL0 (with a proper SIGNAL_CAP) as we do in CRIU in the restore
flow.
I've written a minimal selftest to help me get a better understanding
of what's happening under the hood when using GCSSS1 and GCSSTR as
well as remapping the shadow stack VMA. The test is available here:
https://github.com/svilenkov/gcs-play
I plan to present this work done with CRIU at the Linux Plumbers
Conference this year (Containers and Checkpoint/Restore MC). If
there's also an Arm64 MC or any Arm-related session, I'd love to
attend it.
--
Thanks,
Igor Svilenkov Bozic | CRIU / Arm64 GCS
More information about the linux-arm-kernel
mailing list