[PATCH v2 1/3] arm64/gcs: Ignore attempts to change GCS mode during signal return

From: Mark Brown

Date: Thu Nov 05 2026 - 08:24:31 EST


If userspace tries to update the GCS mode as part of signal return then
we update the thread state but don't update GCSCRE0_EL1, meaning the
change does not take effect. We may also get issues for other tasks on
context switch since we suppress writes of the register if the two tasks
have the same mode.

Since no currently known userspace tries to use this functionality close
this by ignoring any attempt to change the mode through signal return.
The GCS mode for the current thread can be changed using the prctl()
interface and will be preserved on signal return, allowing updates to
the mode from the signal handler.

Reported-by: Catalin Marinas <catalin.marinas@xxxxxxx>
Link: https://lore.kernel.org/r/as-8IqsbMbOBo5Oq@xxxxxxx
Suggested-by: Catalin Marinas <catalin.marinas@xxxxxxx>
Fixes: 16f47bb9ac8a ("arm64/signal: Expose GCS state in signal frames")
Signed-off-by: Mark Brown <broonie@xxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
---
Documentation/arch/arm64/gcs.rst | 13 ++++---------
arch/arm64/kernel/signal.c | 22 +---------------------
2 files changed, 5 insertions(+), 30 deletions(-)

diff --git a/Documentation/arch/arm64/gcs.rst b/Documentation/arch/arm64/gcs.rst
index 226c0b008456..f2bcd66328bb 100644
--- a/Documentation/arch/arm64/gcs.rst
+++ b/Documentation/arch/arm64/gcs.rst
@@ -179,9 +179,8 @@ intended to be exhaustive.

When returning from a signal handler:

-* If there is a gcs_context record in the signal frame then the GCS flags
- and GCSPR_EL0 will be restored from that context prior to further
- validation.
+* If there is a gcs_context record in the signal frame then GCSPR_EL0
+ will be restored from that context prior to further validation.

* If there is no gcs_context record in the signal frame then the GCS
configuration will be unchanged.
@@ -190,12 +189,8 @@ When returning from a signal handler:
point to a valid GCS signal cap record, this will be popped from the
GCS prior to signal return.

-* If the GCS configuration is locked when returning from a signal then any
- attempt to change the GCS configuration will be treated as an error. This
- is true even if GCS was not enabled prior to signal entry.
-
-* GCS may be disabled via signal return but any attempt to enable GCS via
- signal return will be rejected.
+* The features_enabled in the signal return context will be ignored, any
+ changes made using the prctl() in the signal handler will be preserved.


6. ptrace extensions
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 6a06a4e352b1..80398ac83c3b 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -741,36 +741,16 @@ static int preserve_gcs_context(struct gcs_context __user *ctx)

static int restore_gcs_context(struct user_ctxs *user)
{
- u64 gcspr, enabled;
+ u64 gcspr;
int err = 0;

if (user->gcs_size != sizeof(*user->gcs))
return -EINVAL;

__get_user_error(gcspr, &user->gcs->gcspr, err);
- __get_user_error(enabled, &user->gcs->features_enabled, err);
if (err)
return err;

- /* Don't allow unknown modes */
- if (enabled & ~PR_SHADOW_STACK_SUPPORTED_STATUS_MASK)
- return -EINVAL;
-
- err = gcs_check_locked(current, enabled);
- if (err != 0)
- return err;
-
- /* Don't allow enabling */
- if (!task_gcs_el0_enabled(current) &&
- (enabled & PR_SHADOW_STACK_ENABLE))
- return -EINVAL;
-
- /* If we are disabling disable everything */
- if (!(enabled & PR_SHADOW_STACK_ENABLE))
- enabled = 0;
-
- current->thread.gcs_el0_mode = enabled;
-
/*
* We let userspace set GCSPR_EL0 to anything here, we will
* validate later in gcs_restore_signal().

--
2.47.3