]> Git Repo - linux.git/blobdiff - arch/arm64/kernel/signal.c
Merge branches 'for-next/gcs', 'for-next/probes', 'for-next/asm-offsets', 'for-next...
[linux.git] / arch / arm64 / kernel / signal.c
index 5619869475304776fc005fe24a385bf86bfdd253..2eb2e97a934f662a9e15815d93e73de8e27d5ffd 100644 (file)
@@ -25,6 +25,7 @@
 #include <asm/elf.h>
 #include <asm/exception.h>
 #include <asm/cacheflush.h>
+#include <asm/gcs.h>
 #include <asm/ucontext.h>
 #include <asm/unistd.h>
 #include <asm/fpsimd.h>
 #include <asm/traps.h>
 #include <asm/vdso.h>
 
+#ifdef CONFIG_ARM64_GCS
+#define GCS_SIGNAL_CAP(addr) (((unsigned long)addr) & GCS_CAP_ADDR_MASK)
+
+static bool gcs_signal_cap_valid(u64 addr, u64 val)
+{
+       return val == GCS_SIGNAL_CAP(addr);
+}
+#endif
+
 /*
  * Do a signal return; undo the signal stack. These are aligned to 128-bit.
  */
@@ -42,11 +52,6 @@ struct rt_sigframe {
        struct ucontext uc;
 };
 
-struct frame_record {
-       u64 fp;
-       u64 lr;
-};
-
 struct rt_sigframe_user_layout {
        struct rt_sigframe __user *sigframe;
        struct frame_record __user *next_frame;
@@ -56,6 +61,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;
@@ -188,6 +194,8 @@ struct user_ctxs {
        u32 fpmr_size;
        struct poe_context __user *poe;
        u32 poe_size;
+       struct gcs_context __user *gcs;
+       u32 gcs_size;
 };
 
 static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
@@ -633,6 +641,82 @@ 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 = read_sysreg_s(SYS_GCSPR_EL0);
+
+       /*
+        * If GCS is enabled we will add a cap token to the frame,
+        * include it in the GCSPR_EL0 we report to support stack
+        * switching via sigreturn if GCS is enabled.  We do not allow
+        * enabling via sigreturn so the token is only relevant for
+        * threads with GCS enabled.
+        */
+       if (task_gcs_el0_enabled(current))
+               gcspr -= 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().
+        */
+       write_sysreg_s(gcspr, 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)
 {
@@ -651,6 +735,7 @@ static int parse_user_sigframe(struct user_ctxs *user,
        user->zt = NULL;
        user->fpmr = NULL;
        user->poe = NULL;
+       user->gcs = NULL;
 
        if (!IS_ALIGNED((unsigned long)base, 16))
                goto invalid;
@@ -767,6 +852,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;
@@ -886,6 +982,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);
 
@@ -904,6 +1003,58 @@ static int restore_sigframe(struct pt_regs *regs,
        return err;
 }
 
+#ifdef CONFIG_ARM64_GCS
+static int gcs_restore_signal(void)
+{
+       unsigned long __user *gcspr_el0;
+       u64 cap;
+       int ret;
+
+       if (!system_supports_gcs())
+               return 0;
+
+       if (!(current->thread.gcs_el0_mode & PR_SHADOW_STACK_ENABLE))
+               return 0;
+
+       gcspr_el0 = (unsigned long __user *)read_sysreg_s(SYS_GCSPR_EL0);
+
+       /*
+        * Ensure that any changes to the GCS done via GCS operations
+        * are visible to the normal reads we do to validate the
+        * token.
+        */
+       gcsb_dsync();
+
+       /*
+        * GCSPR_EL0 should be pointing at a capped GCS, read the cap.
+        * We don't enforce that this is in a GCS page, if it is not
+        * then faults will be generated on GCS operations - the main
+        * concern is to protect GCS pages.
+        */
+       ret = copy_from_user(&cap, gcspr_el0, sizeof(cap));
+       if (ret)
+               return -EFAULT;
+
+       /*
+        * Check that the cap is the actual GCS before replacing it.
+        */
+       if (!gcs_signal_cap_valid((u64)gcspr_el0, cap))
+               return -EINVAL;
+
+       /* Invalidate the token to prevent reuse */
+       put_user_gcs(0, (__user void*)gcspr_el0, &ret);
+       if (ret != 0)
+               return -EFAULT;
+
+       write_sysreg_s(gcspr_el0 + 1, SYS_GCSPR_EL0);
+
+       return 0;
+}
+
+#else
+static int gcs_restore_signal(void) { return 0; }
+#endif
+
 SYSCALL_DEFINE0(rt_sigreturn)
 {
        struct pt_regs *regs = current_pt_regs();
@@ -927,6 +1078,9 @@ SYSCALL_DEFINE0(rt_sigreturn)
        if (restore_sigframe(regs, frame))
                goto badframe;
 
+       if (gcs_restore_signal())
+               goto badframe;
+
        if (restore_altstack(&frame->uc.uc_stack))
                goto badframe;
 
@@ -964,6 +1118,15 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
                        return err;
        }
 
+#ifdef CONFIG_ARM64_GCS
+       if (system_supports_gcs() && (add_all || current->thread.gcspr_el0)) {
+               err = sigframe_alloc(user, &user->gcs_offset,
+                                    sizeof(struct gcs_context));
+               if (err)
+                       return err;
+       }
+#endif
+
        if (system_supports_sve() || system_supports_sme()) {
                unsigned int vq = 0;
 
@@ -1071,6 +1234,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) {
@@ -1189,7 +1358,48 @@ static int get_sigframe(struct rt_sigframe_user_layout *user,
        return 0;
 }
 
-static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
+#ifdef CONFIG_ARM64_GCS
+
+static int gcs_signal_entry(__sigrestore_t sigtramp, struct ksignal *ksig)
+{
+       unsigned long __user *gcspr_el0;
+       int ret = 0;
+
+       if (!system_supports_gcs())
+               return 0;
+
+       if (!task_gcs_el0_enabled(current))
+               return 0;
+
+       /*
+        * We are entering a signal handler, current register state is
+        * active.
+        */
+       gcspr_el0 = (unsigned long __user *)read_sysreg_s(SYS_GCSPR_EL0);
+
+       /*
+        * Push a cap and the GCS entry for the trampoline onto the GCS.
+        */
+       put_user_gcs((unsigned long)sigtramp, gcspr_el0 - 2, &ret);
+       put_user_gcs(GCS_SIGNAL_CAP(gcspr_el0 - 1), gcspr_el0 - 1, &ret);
+       if (ret != 0)
+               return ret;
+
+       gcspr_el0 -= 2;
+       write_sysreg_s((unsigned long)gcspr_el0, SYS_GCSPR_EL0);
+
+       return 0;
+}
+#else
+
+static int gcs_signal_entry(__sigrestore_t sigtramp, struct ksignal *ksig)
+{
+       return 0;
+}
+
+#endif
+
+static int setup_return(struct pt_regs *regs, struct ksignal *ksig,
                         struct rt_sigframe_user_layout *user, int usig)
 {
        __sigrestore_t sigtramp;
@@ -1197,7 +1407,7 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
        regs->regs[0] = usig;
        regs->sp = (unsigned long)user->sigframe;
        regs->regs[29] = (unsigned long)&user->next_frame->fp;
-       regs->pc = (unsigned long)ka->sa.sa_handler;
+       regs->pc = (unsigned long)ksig->ka.sa.sa_handler;
 
        /*
         * Signal delivery is a (wacky) indirect function call in
@@ -1240,12 +1450,14 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
        if (system_supports_poe())
                write_sysreg_s(POR_EL0_INIT, SYS_POR_EL0);
 
-       if (ka->sa.sa_flags & SA_RESTORER)
-               sigtramp = ka->sa.sa_restorer;
+       if (ksig->ka.sa.sa_flags & SA_RESTORER)
+               sigtramp = ksig->ka.sa.sa_restorer;
        else
                sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp);
 
        regs->regs[30] = (unsigned long)sigtramp;
+
+       return gcs_signal_entry(sigtramp, ksig);
 }
 
 static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
@@ -1268,7 +1480,7 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
        err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
        err |= setup_sigframe(&user, regs, set);
        if (err == 0) {
-               setup_return(regs, &ksig->ka, &user, usig);
+               err = setup_return(regs, ksig, &user, usig);
                if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
                        err |= copy_siginfo_to_user(&frame->info, &ksig->info);
                        regs->regs[1] = (unsigned long)&frame->info;
This page took 0.041927 seconds and 4 git commands to generate.