arm64: Add support for PR_SPEC_DISABLE_NOEXEC prctl() option
authorWill Deacon <will@kernel.org>
Mon, 28 Sep 2020 13:03:00 +0000 (14:03 +0100)
committerWill Deacon <will@kernel.org>
Tue, 29 Sep 2020 15:08:17 +0000 (16:08 +0100)
The PR_SPEC_DISABLE_NOEXEC option to the PR_SPEC_STORE_BYPASS prctl()
allows the SSB mitigation to be enabled only until the next execve(),
at which point the state will revert back to PR_SPEC_ENABLE and the
mitigation will be disabled.

Add support for PR_SPEC_DISABLE_NOEXEC on arm64.

Reported-by: Anthony Steinhauser <asteinhauser@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
arch/arm64/kernel/process.c
arch/arm64/kernel/proton-pack.c

index 9dbd35b95253a7e2c6ef0b295b3e3f9997a6deef..085d8ca39e47df59e80815c5b017184df58e12a3 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/lockdep.h>
 #include <linux/mman.h>
 #include <linux/mm.h>
+#include <linux/nospec.h>
 #include <linux/stddef.h>
 #include <linux/sysctl.h>
 #include <linux/unistd.h>
@@ -609,6 +610,11 @@ void arch_setup_new_exec(void)
        current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
 
        ptrauth_thread_init_user(current);
+
+       if (task_spec_ssb_noexec(current)) {
+               arch_prctl_spec_ctrl_set(current, PR_SPEC_STORE_BYPASS,
+                                        PR_SPEC_ENABLE);
+       }
 }
 
 #ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
index 16c9a095a74684c8620a684b533da20bfbb4b690..68b710f1b43f343ad5f20f196314bef55410548f 100644 (file)
@@ -660,6 +660,20 @@ void spectre_v4_enable_task_mitigation(struct task_struct *tsk)
  * prctl() may be necessary even when PSTATE.SSBS can be toggled directly
  * from userspace.
  */
+static void ssbd_prctl_enable_mitigation(struct task_struct *task)
+{
+       task_clear_spec_ssb_noexec(task);
+       task_set_spec_ssb_disable(task);
+       set_tsk_thread_flag(task, TIF_SSBD);
+}
+
+static void ssbd_prctl_disable_mitigation(struct task_struct *task)
+{
+       task_clear_spec_ssb_noexec(task);
+       task_clear_spec_ssb_disable(task);
+       clear_tsk_thread_flag(task, TIF_SSBD);
+}
+
 static int ssbd_prctl_set(struct task_struct *task, unsigned long ctrl)
 {
        switch (ctrl) {
@@ -679,8 +693,7 @@ static int ssbd_prctl_set(struct task_struct *task, unsigned long ctrl)
                if (spectre_v4_mitigations_on())
                        return -EPERM;
 
-               task_clear_spec_ssb_disable(task);
-               clear_tsk_thread_flag(task, TIF_SSBD);
+               ssbd_prctl_disable_mitigation(task);
                break;
        case PR_SPEC_FORCE_DISABLE:
                /* Force disable speculation: force enable mitigation */
@@ -699,8 +712,22 @@ static int ssbd_prctl_set(struct task_struct *task, unsigned long ctrl)
                if (spectre_v4_mitigations_off())
                        return -EPERM;
 
-               task_set_spec_ssb_disable(task);
-               set_tsk_thread_flag(task, TIF_SSBD);
+               ssbd_prctl_enable_mitigation(task);
+               break;
+       case PR_SPEC_DISABLE_NOEXEC:
+               /* Disable speculation until execve(): enable mitigation */
+               /*
+                * If the mitigation state is forced one way or the other, then
+                * we must fail now before we try to toggle it on execve().
+                */
+               if (task_spec_ssb_force_disable(task) ||
+                   spectre_v4_mitigations_off() ||
+                   spectre_v4_mitigations_on()) {
+                       return -EPERM;
+               }
+
+               ssbd_prctl_enable_mitigation(task);
+               task_set_spec_ssb_noexec(task);
                break;
        default:
                return -ERANGE;
@@ -745,6 +772,9 @@ static int ssbd_prctl_get(struct task_struct *task)
        if (task_spec_ssb_force_disable(task))
                return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE;
 
+       if (task_spec_ssb_noexec(task))
+               return PR_SPEC_PRCTL | PR_SPEC_DISABLE_NOEXEC;
+
        if (task_spec_ssb_disable(task))
                return PR_SPEC_PRCTL | PR_SPEC_DISABLE;