diff mbox series

[v10,24/40] arm64/signal: Expose GCS state in signal frames

Message ID 20240801-arm64-gcs-v10-24-699e2bd2190b@kernel.org (mailing list archive)
State New
Headers show
Series arm64/gcs: Provide support for GCS in userspace | expand

Commit Message

Mark Brown Aug. 1, 2024, 12:06 p.m. UTC
Add a context for the GCS state and include it in the signal context when
running on a system that supports GCS. We reuse the same flags that the
prctl() uses to specify which GCS features are enabled and also provide the
current GCS pointer.

We do not support enabling GCS via signal return, there is a conflict
between specifying GCSPR_EL0 and allocation of a new GCS and this is not
an ancticipated use case.  We also enforce GCS configuration locking on
signal return.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 arch/arm64/include/uapi/asm/sigcontext.h |   9 +++
 arch/arm64/kernel/signal.c               | 106 +++++++++++++++++++++++++++++++
 2 files changed, 115 insertions(+)

Comments

Dave Martin Aug. 14, 2024, 3:09 p.m. UTC | #1
On Thu, Aug 01, 2024 at 01:06:51PM +0100, Mark Brown wrote:
> Add a context for the GCS state and include it in the signal context when
> running on a system that supports GCS. We reuse the same flags that the
> prctl() uses to specify which GCS features are enabled and also provide the
> current GCS pointer.
> 
> We do not support enabling GCS via signal return, there is a conflict
> between specifying GCSPR_EL0 and allocation of a new GCS and this is not
> an ancticipated use case.  We also enforce GCS configuration locking on
> signal return.
> 
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> ---
>  arch/arm64/include/uapi/asm/sigcontext.h |   9 +++
>  arch/arm64/kernel/signal.c               | 106 +++++++++++++++++++++++++++++++
>  2 files changed, 115 insertions(+)

[...]

> diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c

[...]

> @@ -999,6 +1092,13 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
>  			return err;
>  	}
>  
> +	if (add_all || task_gcs_el0_enabled(current)) {
> +		err = sigframe_alloc(user, &user->gcs_offset,
> +				     sizeof(struct gcs_context));
> +		if (err)
> +			return err;
> +	}
> +

Who turns on GCS?  I have a concern that if libc is new enough to be
built for GCS then the libc startup code will to turn it on, even if
the binary stack running on top of libc is old.

Whether a given library should break old binaries is a bit of a grey
area, but I think that libraries that deliberately export stable ABIs
probably shouldn't.


With that in mind, does any GCS state need to be saved at all?

Is there any scenario where it is legitimate for the signal handler to
change the shadow stack mode or to return with an altered GCSPR_EL0?

Is the guarded stack considered necessary (or at least beneficial) for
backtracing, or is the regular stack sufficient?

(I'm assuming that unwind tables / debug info should allow the shadow
stack to be unwound anyway; rather this question is about whether
software can straightforwardly find out the interrupted GCSPR_EL0
without this information... and whether it needs to.)


>  	if (system_supports_sve() || system_supports_sme()) {
>  		unsigned int vq = 0;
>  
> @@ -1099,6 +1199,12 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
>  		__put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
>  	}
>  
> +	if (system_supports_gcs() && err == 0 && user->gcs_offset) {
> +		struct gcs_context __user *gcs_ctx =
> +			apply_user_offset(user, user->gcs_offset);
> +		err |= preserve_gcs_context(gcs_ctx);
> +	}
> +

[...]

Cheers
---Dave
Mark Brown Aug. 14, 2024, 4:21 p.m. UTC | #2
On Wed, Aug 14, 2024 at 04:09:51PM +0100, Dave Martin wrote:
> On Thu, Aug 01, 2024 at 01:06:51PM +0100, Mark Brown wrote:

> > +	if (add_all || task_gcs_el0_enabled(current)) {
> > +		err = sigframe_alloc(user, &user->gcs_offset,
> > +				     sizeof(struct gcs_context));
> > +		if (err)
> > +			return err;
> > +	}

> Who turns on GCS?  I have a concern that if libc is new enough to be
> built for GCS then the libc startup code will to turn it on, even if
> the binary stack running on top of libc is old.

It should normally be the dynamic linker which should be looking for
annotatations in the binaries it's loading before it decides if it's
going to turn on GCS (and libc doing something similar if it's going to
dlopen() things in a process with GCS enabled).

> Is there any scenario where it is legitimate for the signal handler to
> change the shadow stack mode or to return with an altered GCSPR_EL0?

If userspace can rewrite the stack pointer on return (eg, to return to a
different context as part of userspace threading) then it will need to
be able to also update GCSPR_EL0 to something consistent otherwise
attempting to return from the interrupted context isn't going to go
well.  Changing the mode is a bit more exotic, as it is in general.
It's as much to provide information to the signal handler as anything
else.

> Is the guarded stack considered necessary (or at least beneficial) for
> backtracing, or is the regular stack sufficient?

It's potentially beneficial, being less vulnerable to corruption and
simpler to parse if all you're interested in is return addresses.
Profiling in particular was mentioned, grabbing a linear block of memory
will hopefully be less overhead than chasing down the stack.  The
regular stack should generally be sufficient though.
Dave Martin Aug. 15, 2024, 2:01 p.m. UTC | #3
On Wed, Aug 14, 2024 at 05:21:44PM +0100, Mark Brown wrote:
> On Wed, Aug 14, 2024 at 04:09:51PM +0100, Dave Martin wrote:
> > On Thu, Aug 01, 2024 at 01:06:51PM +0100, Mark Brown wrote:
> 
> > > +	if (add_all || task_gcs_el0_enabled(current)) {
> > > +		err = sigframe_alloc(user, &user->gcs_offset,
> > > +				     sizeof(struct gcs_context));
> > > +		if (err)
> > > +			return err;
> > > +	}
> 
> > Who turns on GCS?  I have a concern that if libc is new enough to be
> > built for GCS then the libc startup code will to turn it on, even if
> > the binary stack running on top of libc is old.
> 
> It should normally be the dynamic linker which should be looking for
> annotatations in the binaries it's loading before it decides if it's
> going to turn on GCS (and libc doing something similar if it's going to
> dlopen() things in a process with GCS enabled).

My thought was that if libc knows about shadow stacks, it is probably
going to be built to use them too and so would enable shadow stack
during startup to protect its own code.

(Technically those would be independent decisions, but it seems a good
idea to use a hardening feature if you know about and it is present.)


If so, shadow stacks might always get turned on before the main program
gets a look-in.

Or is that not the expectation?

> 
> > Is there any scenario where it is legitimate for the signal handler to
> > change the shadow stack mode or to return with an altered GCSPR_EL0?
> 
> If userspace can rewrite the stack pointer on return (eg, to return to a
> different context as part of userspace threading) then it will need to

Do we know if code that actually does that?  IIUC, trying to do this is
totally broken on most arches nowadays; making it work requires a
reentrant C library and/or logic to defer signals around critical
sections in userspace.

"Real" threading makes this pretty pointless for the most part.


Related question: does shadow stack work with ucontext-based coroutines?
Per-context stacks need to be allocated by the program for that.


> be able to also update GCSPR_EL0 to something consistent otherwise
> attempting to return from the interrupted context isn't going to go
> well.  Changing the mode is a bit more exotic, as it is in general.
> It's as much to provide information to the signal handler as anything
> else.

I'm not sure that we should always put information in the signal frame
that the signal handler can't obtain directly.

I guess it's harmless to include this, though.

> > Is the guarded stack considered necessary (or at least beneficial) for
> > backtracing, or is the regular stack sufficient?
> 
> It's potentially beneficial, being less vulnerable to corruption and
> simpler to parse if all you're interested in is return addresses.
> Profiling in particular was mentioned, grabbing a linear block of memory
> will hopefully be less overhead than chasing down the stack.  The
> regular stack should generally be sufficient though.

I guess we can't really argue that the shadow stack pointer is
redundant here though.  The whole point of shadow stacks is to make
things more robust...

Just kicking the tyres on the question of whether we need it here, but
I guess it's hard to make a good case for saying "no".

Cheers
---Dave
Mark Brown Aug. 15, 2024, 3:05 p.m. UTC | #4
On Thu, Aug 15, 2024 at 03:01:21PM +0100, Dave Martin wrote:

> My thought was that if libc knows about shadow stacks, it is probably
> going to be built to use them too and so would enable shadow stack
> during startup to protect its own code.

> (Technically those would be independent decisions, but it seems a good
> idea to use a hardening feature if you know about and it is present.)

> If so, shadow stacks might always get turned on before the main program
> gets a look-in.

> Or is that not the expectation?

The expectation (at least for arm64) is that the main program will only
have shadow stacks if everything says it can support them.  If the
dynamic linker turns them on during startup prior to parsing the main
executables this means that it should turn them off before actually
starting the executable, taking care to consider any locking of features.

> > > Is there any scenario where it is legitimate for the signal handler to
> > > change the shadow stack mode or to return with an altered GCSPR_EL0?

> > If userspace can rewrite the stack pointer on return (eg, to return to a
> > different context as part of userspace threading) then it will need to

> Do we know if code that actually does that?  IIUC, trying to do this is
> totally broken on most arches nowadays; making it work requires a
> reentrant C library and/or logic to defer signals around critical
> sections in userspace.

> "Real" threading makes this pretty pointless for the most part.

> Related question: does shadow stack work with ucontext-based coroutines?
> Per-context stacks need to be allocated by the program for that.

Yes, ucontext based coroutines are the sort of thing I meant when I was
talking about returning to a different context?  

> > be able to also update GCSPR_EL0 to something consistent otherwise
> > attempting to return from the interrupted context isn't going to go
> > well.  Changing the mode is a bit more exotic, as it is in general.
> > It's as much to provide information to the signal handler as anything
> > else.

> I'm not sure that we should always put information in the signal frame
> that the signal handler can't obtain directly.

> I guess it's harmless to include this, though.

If we don't include it then if different ucontexts have different GCS
features enabled we run into trouble on context switch.

> > > Is the guarded stack considered necessary (or at least beneficial) for
> > > backtracing, or is the regular stack sufficient?

> > It's potentially beneficial, being less vulnerable to corruption and
> > simpler to parse if all you're interested in is return addresses.
> > Profiling in particular was mentioned, grabbing a linear block of memory
> > will hopefully be less overhead than chasing down the stack.  The
> > regular stack should generally be sufficient though.

> I guess we can't really argue that the shadow stack pointer is
> redundant here though.  The whole point of shadow stacks is to make
> things more robust...

> Just kicking the tyres on the question of whether we need it here, but
> I guess it's hard to make a good case for saying "no".

Indeed.  The general model here is that we don't break userspace that
relies on parses the normal stack (so the GCS is never *necessary*) but
clearly you want to have it.
Dave Martin Aug. 15, 2024, 3:33 p.m. UTC | #5
On Thu, Aug 15, 2024 at 04:05:32PM +0100, Mark Brown wrote:
> On Thu, Aug 15, 2024 at 03:01:21PM +0100, Dave Martin wrote:
> 
> > My thought was that if libc knows about shadow stacks, it is probably
> > going to be built to use them too and so would enable shadow stack
> > during startup to protect its own code.
> 
> > (Technically those would be independent decisions, but it seems a good
> > idea to use a hardening feature if you know about and it is present.)
> 
> > If so, shadow stacks might always get turned on before the main program
> > gets a look-in.
> 
> > Or is that not the expectation?
> 
> The expectation (at least for arm64) is that the main program will only
> have shadow stacks if everything says it can support them.  If the
> dynamic linker turns them on during startup prior to parsing the main
> executables this means that it should turn them off before actually
> starting the executable, taking care to consider any locking of features.

Hmm, so we really do get a clear "enable shadow stack" call to the
kernel, which we can reasonaly expect won't happen for ancient software?

If so, I think dumping the GCS state in the sigframe could be made
conditional on that without problems (?)

(We could always make it unconditional later if it turn out that that
approach breaks something.)

> 
> > > > Is there any scenario where it is legitimate for the signal handler to
> > > > change the shadow stack mode or to return with an altered GCSPR_EL0?
> 
> > > If userspace can rewrite the stack pointer on return (eg, to return to a
> > > different context as part of userspace threading) then it will need to
> 
> > Do we know if code that actually does that?  IIUC, trying to do this is
> > totally broken on most arches nowadays; making it work requires a
> > reentrant C library and/or logic to defer signals around critical
> > sections in userspace.
> 
> > "Real" threading makes this pretty pointless for the most part.
> 
> > Related question: does shadow stack work with ucontext-based coroutines?
> > Per-context stacks need to be allocated by the program for that.
> 
> Yes, ucontext based coroutines are the sort of thing I meant when I was
> talking about returning to a different context?  

Ah, right.  Doing this asynchronously on the back of a signal (instead
of doing a sigreturn) is the bad thing.  setcontext() officially
doesn't work for this any more, and doing it by hacking or rebuilding
the sigframe is extremely hairy and probably a terrible idea for the
reasons I gave.

> > > be able to also update GCSPR_EL0 to something consistent otherwise
> > > attempting to return from the interrupted context isn't going to go
> > > well.  Changing the mode is a bit more exotic, as it is in general.
> > > It's as much to provide information to the signal handler as anything
> > > else.

Note, the way sigcontext (a.k.a. mcontext).__reserved[] is used by
glibc for the ucontext API is inspired by the way the kernel uses it,
but not guaranteed to be compatible.  For the ucontext API glibc
doesn't try to store/restore asynchronous contexts (which is why
setcontext() from a signal handler is totally broken), so there is no
need to store SVE/SME state and hence lots of free space, so this
probably is supportable with shadow stacks -- if there's a way to
allocate them.  This series would be unaffected either way.

(IIRC, the contents of mcontext.__reserved[] is totally incompatible
with what the kernel puts in there, and doesn't have the same record
structure.)

> 
> > I'm not sure that we should always put information in the signal frame
> > that the signal handler can't obtain directly.
> 
> > I guess it's harmless to include this, though.
> 
> If we don't include it then if different ucontexts have different GCS
> features enabled we run into trouble on context switch.

As outlined above, nowadays you can only use setcontext() on a context
obtained from getcontext().  Using setcontext() on a context obtained
from a sigframe works by accident or not at all, but in any case
coroutines always switch synchronously and don't rely on doing this.

(See where setcontext deals with the FPSIMD regs:
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/aarch64/setcontext.S;h=ba659438c564dc3bbbb8d6039030e2c492649534;hb=HEAD )

So, overall I think making ucontext coroutines with with GCS is purely
a libc matter that is "interesting" here, but we don't need to worry
about.

> > > > Is the guarded stack considered necessary (or at least beneficial) for
> > > > backtracing, or is the regular stack sufficient?
> 
> > > It's potentially beneficial, being less vulnerable to corruption and
> > > simpler to parse if all you're interested in is return addresses.
> > > Profiling in particular was mentioned, grabbing a linear block of memory
> > > will hopefully be less overhead than chasing down the stack.  The
> > > regular stack should generally be sufficient though.
> 
> > I guess we can't really argue that the shadow stack pointer is
> > redundant here though.  The whole point of shadow stacks is to make
> > things more robust...
> 
> > Just kicking the tyres on the question of whether we need it here, but
> > I guess it's hard to make a good case for saying "no".
> 
> Indeed.  The general model here is that we don't break userspace that
> relies on parses the normal stack (so the GCS is never *necessary*) but
> clearly you want to have it.

Agreed, but perhaps not in programs that haven't enabled shadow stack?

Cheers
---Dave
Mark Brown Aug. 15, 2024, 3:46 p.m. UTC | #6
On Thu, Aug 15, 2024 at 04:33:25PM +0100, Dave Martin wrote:
> On Thu, Aug 15, 2024 at 04:05:32PM +0100, Mark Brown wrote:

> > The expectation (at least for arm64) is that the main program will only
> > have shadow stacks if everything says it can support them.  If the
> > dynamic linker turns them on during startup prior to parsing the main
> > executables this means that it should turn them off before actually
> > starting the executable, taking care to consider any locking of features.

> Hmm, so we really do get a clear "enable shadow stack" call to the
> kernel, which we can reasonaly expect won't happen for ancient software?

Yes, userspace always has to explicitly enable the GCS.

> If so, I think dumping the GCS state in the sigframe could be made
> conditional on that without problems (?)

It is - we only allocate the sigframe if the task has GCS enabled.

> > > Related question: does shadow stack work with ucontext-based coroutines?
> > > Per-context stacks need to be allocated by the program for that.

> > Yes, ucontext based coroutines are the sort of thing I meant when I was
> > talking about returning to a different context?  

> Ah, right.  Doing this asynchronously on the back of a signal (instead
> of doing a sigreturn) is the bad thing.  setcontext() officially
> doesn't work for this any more, and doing it by hacking or rebuilding
> the sigframe is extremely hairy and probably a terrible idea for the
> reasons I gave.

I see.  I tend to view this as more adventurous than I personally would
be when writing userspace code but equally I don't see a need to
actively break things.  There's no *requirement* to use libc...

> So, overall I think making ucontext coroutines with with GCS is purely
> a libc matter that is "interesting" here, but we don't need to worry
> about.

Yes, it's not our problem so long as we don't get in the way somehow.
Dave Martin Aug. 15, 2024, 4:40 p.m. UTC | #7
On Thu, Aug 15, 2024 at 04:46:04PM +0100, Mark Brown wrote:
> On Thu, Aug 15, 2024 at 04:33:25PM +0100, Dave Martin wrote:
> > On Thu, Aug 15, 2024 at 04:05:32PM +0100, Mark Brown wrote:
> 
> > > The expectation (at least for arm64) is that the main program will only
> > > have shadow stacks if everything says it can support them.  If the
> > > dynamic linker turns them on during startup prior to parsing the main
> > > executables this means that it should turn them off before actually
> > > starting the executable, taking care to consider any locking of features.
> 
> > Hmm, so we really do get a clear "enable shadow stack" call to the
> > kernel, which we can reasonaly expect won't happen for ancient software?
> 
> Yes, userspace always has to explicitly enable the GCS.
> 
> > If so, I think dumping the GCS state in the sigframe could be made
> > conditional on that without problems (?)
> 
> It is - we only allocate the sigframe if the task has GCS enabled.

OK, makes sense.

> > > > Related question: does shadow stack work with ucontext-based coroutines?
> > > > Per-context stacks need to be allocated by the program for that.
> 
> > > Yes, ucontext based coroutines are the sort of thing I meant when I was
> > > talking about returning to a different context?  
> 
> > Ah, right.  Doing this asynchronously on the back of a signal (instead
> > of doing a sigreturn) is the bad thing.  setcontext() officially
> > doesn't work for this any more, and doing it by hacking or rebuilding
> > the sigframe is extremely hairy and probably a terrible idea for the
> > reasons I gave.
> 
> I see.  I tend to view this as more adventurous than I personally would
> be when writing userspace code but equally I don't see a need to
> actively break things.  There's no *requirement* to use libc...
> 
> > So, overall I think making ucontext coroutines with with GCS is purely
> > a libc matter that is "interesting" here, but we don't need to worry
> > about.
> 
> Yes, it's not our problem so long as we don't get in the way somehow.

Sure.  "Hairy and probably a terrible idea" is not the same as
"impossible", but you need to know what you're doing and you get
exposed to all sorts of portability challenges.

There's a limit to how much we should attempt to smooth over all that.

Anyway, I think what the GCS patches are doing looks reasonable.

Cheers
---Dave
Catalin Marinas Aug. 21, 2024, 5:40 p.m. UTC | #8
On Thu, Aug 01, 2024 at 01:06:51PM +0100, Mark Brown wrote:
> @@ -636,6 +639,81 @@ extern int restore_zt_context(struct user_ctxs *user);
>  
>  #endif /* ! CONFIG_ARM64_SME */
>  
> +#ifdef CONFIG_ARM64_GCS
> +
> +static int preserve_gcs_context(struct gcs_context __user *ctx)
> +{
> +	int err = 0;
> +	u64 gcspr;
> +
> +	/*
> +	 * We will add a cap token to the frame, include it in the
> +	 * GCSPR_EL0 we report to support stack switching via
> +	 * sigreturn.
> +	 */
> +	gcs_preserve_current_state();
> +	gcspr = current->thread.gcspr_el0 - 8;

We discussed briefly offline. Not a problem in this patch but
gcs_preserve_current_state() only saves it conditionally on GCS being
enabled for the task. However, you mentioned that the register is always
available to the user, so I'd rather change the preserving function to
save it unconditionally.

> +
> +	__put_user_error(GCS_MAGIC, &ctx->head.magic, err);
> +	__put_user_error(sizeof(*ctx), &ctx->head.size, err);
> +	__put_user_error(gcspr, &ctx->gcspr, err);
> +	__put_user_error(0, &ctx->reserved, err);
> +	__put_user_error(current->thread.gcs_el0_mode,
> +			 &ctx->features_enabled, err);
> +
> +	return err;
> +}
> +
> +static int restore_gcs_context(struct user_ctxs *user)
> +{
> +	u64 gcspr, enabled;
> +	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;

We don't allow enabling and that's fine but we don't restore gcspr
either with this early return.

> +
> +	/* 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().
> +	 */
> +	current->thread.gcspr_el0 = gcspr;
> +	write_sysreg_s(current->thread.gcspr_el0, SYS_GCSPR_EL0);

I think you should move this further up unconditionally.
diff mbox series

Patch

diff --git a/arch/arm64/include/uapi/asm/sigcontext.h b/arch/arm64/include/uapi/asm/sigcontext.h
index 8a45b7a411e0..c2d61e8efc84 100644
--- a/arch/arm64/include/uapi/asm/sigcontext.h
+++ b/arch/arm64/include/uapi/asm/sigcontext.h
@@ -176,6 +176,15 @@  struct zt_context {
 	__u16 __reserved[3];
 };
 
+#define GCS_MAGIC	0x47435300
+
+struct gcs_context {
+	struct _aarch64_ctx head;
+	__u64 gcspr;
+	__u64 features_enabled;
+	__u64 reserved;
+};
+
 #endif /* !__ASSEMBLY__ */
 
 #include <asm/sve_context.h>
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index a1e0aa38bff9..6db364beb024 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -88,6 +88,7 @@  struct rt_sigframe_user_layout {
 
 	unsigned long fpsimd_offset;
 	unsigned long esr_offset;
+	unsigned long gcs_offset;
 	unsigned long sve_offset;
 	unsigned long tpidr2_offset;
 	unsigned long za_offset;
@@ -217,6 +218,8 @@  struct user_ctxs {
 	u32 zt_size;
 	struct fpmr_context __user *fpmr;
 	u32 fpmr_size;
+	struct gcs_context __user *gcs;
+	u32 gcs_size;
 };
 
 static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
@@ -636,6 +639,81 @@  extern int restore_zt_context(struct user_ctxs *user);
 
 #endif /* ! CONFIG_ARM64_SME */
 
+#ifdef CONFIG_ARM64_GCS
+
+static int preserve_gcs_context(struct gcs_context __user *ctx)
+{
+	int err = 0;
+	u64 gcspr;
+
+	/*
+	 * We will add a cap token to the frame, include it in the
+	 * GCSPR_EL0 we report to support stack switching via
+	 * sigreturn.
+	 */
+	gcs_preserve_current_state();
+	gcspr = current->thread.gcspr_el0 - 8;
+
+	__put_user_error(GCS_MAGIC, &ctx->head.magic, err);
+	__put_user_error(sizeof(*ctx), &ctx->head.size, err);
+	__put_user_error(gcspr, &ctx->gcspr, err);
+	__put_user_error(0, &ctx->reserved, err);
+	__put_user_error(current->thread.gcs_el0_mode,
+			 &ctx->features_enabled, err);
+
+	return err;
+}
+
+static int restore_gcs_context(struct user_ctxs *user)
+{
+	u64 gcspr, enabled;
+	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().
+	 */
+	current->thread.gcspr_el0 = gcspr;
+	write_sysreg_s(current->thread.gcspr_el0, SYS_GCSPR_EL0);
+
+	return 0;
+}
+
+#else /* ! CONFIG_ARM64_GCS */
+
+/* Turn any non-optimised out attempts to use these into a link error: */
+extern int preserve_gcs_context(void __user *ctx);
+extern int restore_gcs_context(struct user_ctxs *user);
+
+#endif /* ! CONFIG_ARM64_GCS */
+
 static int parse_user_sigframe(struct user_ctxs *user,
 			       struct rt_sigframe __user *sf)
 {
@@ -653,6 +731,7 @@  static int parse_user_sigframe(struct user_ctxs *user,
 	user->za = NULL;
 	user->zt = NULL;
 	user->fpmr = NULL;
+	user->gcs = NULL;
 
 	if (!IS_ALIGNED((unsigned long)base, 16))
 		goto invalid;
@@ -758,6 +837,17 @@  static int parse_user_sigframe(struct user_ctxs *user,
 			user->fpmr_size = size;
 			break;
 
+		case GCS_MAGIC:
+			if (!system_supports_gcs())
+				goto invalid;
+
+			if (user->gcs)
+				goto invalid;
+
+			user->gcs = (struct gcs_context __user *)head;
+			user->gcs_size = size;
+			break;
+
 		case EXTRA_MAGIC:
 			if (have_extra_context)
 				goto invalid;
@@ -877,6 +967,9 @@  static int restore_sigframe(struct pt_regs *regs,
 			err = restore_fpsimd_context(&user);
 	}
 
+	if (err == 0 && system_supports_gcs() && user.gcs)
+		err = restore_gcs_context(&user);
+
 	if (err == 0 && system_supports_tpidr2() && user.tpidr2)
 		err = restore_tpidr2_context(&user);
 
@@ -999,6 +1092,13 @@  static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
 			return err;
 	}
 
+	if (add_all || task_gcs_el0_enabled(current)) {
+		err = sigframe_alloc(user, &user->gcs_offset,
+				     sizeof(struct gcs_context));
+		if (err)
+			return err;
+	}
+
 	if (system_supports_sve() || system_supports_sme()) {
 		unsigned int vq = 0;
 
@@ -1099,6 +1199,12 @@  static int setup_sigframe(struct rt_sigframe_user_layout *user,
 		__put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
 	}
 
+	if (system_supports_gcs() && err == 0 && user->gcs_offset) {
+		struct gcs_context __user *gcs_ctx =
+			apply_user_offset(user, user->gcs_offset);
+		err |= preserve_gcs_context(gcs_ctx);
+	}
+
 	/* Scalable Vector Extension state (including streaming), if present */
 	if ((system_supports_sve() || system_supports_sme()) &&
 	    err == 0 && user->sve_offset) {