x86: Introduce userspace API for shadow stack
authorRick Edgecombe <rick.p.edgecombe@intel.com>
Tue, 13 Jun 2023 00:10:52 +0000 (17:10 -0700)
committerDave Hansen <dave.hansen@linux.intel.com>
Wed, 2 Aug 2023 22:01:50 +0000 (15:01 -0700)
Add three new arch_prctl() handles:

 - ARCH_SHSTK_ENABLE/DISABLE enables or disables the specified
   feature. Returns 0 on success or a negative value on error.

 - ARCH_SHSTK_LOCK prevents future disabling or enabling of the
   specified feature. Returns 0 on success or a negative value
   on error.

The features are handled per-thread and inherited over fork(2)/clone(2),
but reset on exec().

Co-developed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Kees Cook <keescook@chromium.org>
Acked-by: Mike Rapoport (IBM) <rppt@kernel.org>
Tested-by: Pengfei Xu <pengfei.xu@intel.com>
Tested-by: John Allen <john.allen@amd.com>
Tested-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/all/20230613001108.3040476-27-rick.p.edgecombe%40intel.com
arch/x86/include/asm/processor.h
arch/x86/include/asm/shstk.h [new file with mode: 0644]
arch/x86/include/uapi/asm/prctl.h
arch/x86/kernel/Makefile
arch/x86/kernel/process_64.c
arch/x86/kernel/shstk.c [new file with mode: 0644]

index d46300e94f853ae12659b9cf887b0a96444905b6..4e35f40ea1e665f765e6765bdb7646010fece5f9 100644 (file)
@@ -28,6 +28,7 @@ struct vm86;
 #include <asm/unwind_hints.h>
 #include <asm/vmxfeatures.h>
 #include <asm/vdso/processor.h>
+#include <asm/shstk.h>
 
 #include <linux/personality.h>
 #include <linux/cache.h>
@@ -475,6 +476,11 @@ struct thread_struct {
         */
        u32                     pkru;
 
+#ifdef CONFIG_X86_USER_SHADOW_STACK
+       unsigned long           features;
+       unsigned long           features_locked;
+#endif
+
        /* Floating point and extended processor state */
        struct fpu              fpu;
        /*
diff --git a/arch/x86/include/asm/shstk.h b/arch/x86/include/asm/shstk.h
new file mode 100644 (file)
index 0000000..ec75380
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_SHSTK_H
+#define _ASM_X86_SHSTK_H
+
+#ifndef __ASSEMBLY__
+#include <linux/types.h>
+
+struct task_struct;
+
+#ifdef CONFIG_X86_USER_SHADOW_STACK
+long shstk_prctl(struct task_struct *task, int option, unsigned long features);
+void reset_thread_features(void);
+#else
+static inline long shstk_prctl(struct task_struct *task, int option,
+                              unsigned long arg2) { return -EINVAL; }
+static inline void reset_thread_features(void) {}
+#endif /* CONFIG_X86_USER_SHADOW_STACK */
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_X86_SHSTK_H */
index e8d7ebbca1a4dc4120f5ae8c924a9d1c1f35db22..1cd44ecc9ce0e332d8eb8d46a4002644c58982c3 100644 (file)
 #define ARCH_MAP_VDSO_32               0x2002
 #define ARCH_MAP_VDSO_64               0x2003
 
+/* Don't use 0x3001-0x3004 because of old glibcs */
+
 #define ARCH_GET_UNTAG_MASK            0x4001
 #define ARCH_ENABLE_TAGGED_ADDR                0x4002
 #define ARCH_GET_MAX_TAG_BITS          0x4003
 #define ARCH_FORCE_TAGGED_SVA          0x4004
 
+#define ARCH_SHSTK_ENABLE              0x5001
+#define ARCH_SHSTK_DISABLE             0x5002
+#define ARCH_SHSTK_LOCK                        0x5003
+
 #endif /* _ASM_X86_PRCTL_H */
index abee0564b75064b30d6d59971a6133b8a2f9c106..6b6bf47652ee4e89f7d369923600d329b42e8614 100644 (file)
@@ -147,6 +147,8 @@ obj-$(CONFIG_CALL_THUNKS)           += callthunks.o
 
 obj-$(CONFIG_X86_CET)                  += cet.o
 
+obj-$(CONFIG_X86_USER_SHADOW_STACK)    += shstk.o
+
 ###
 # 64 bit specific files
 ifeq ($(CONFIG_X86_64),y)
index 3d181c16a2f67fb5f93dc221d7583b145f576578..0f89aa0186d1e9c844cb71553c8674673bc21ae3 100644 (file)
@@ -515,6 +515,8 @@ start_thread_common(struct pt_regs *regs, unsigned long new_ip,
                load_gs_index(__USER_DS);
        }
 
+       reset_thread_features();
+
        loadsegment(fs, 0);
        loadsegment(es, _ds);
        loadsegment(ds, _ds);
@@ -894,6 +896,10 @@ long do_arch_prctl_64(struct task_struct *task, int option, unsigned long arg2)
                else
                        return put_user(LAM_U57_BITS, (unsigned long __user *)arg2);
 #endif
+       case ARCH_SHSTK_ENABLE:
+       case ARCH_SHSTK_DISABLE:
+       case ARCH_SHSTK_LOCK:
+               return shstk_prctl(task, option, arg2);
        default:
                ret = -EINVAL;
                break;
diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c
new file mode 100644 (file)
index 0000000..41ed655
--- /dev/null
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * shstk.c - Intel shadow stack support
+ *
+ * Copyright (c) 2021, Intel Corporation.
+ * Yu-cheng Yu <yu-cheng.yu@intel.com>
+ */
+
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <asm/prctl.h>
+
+void reset_thread_features(void)
+{
+       current->thread.features = 0;
+       current->thread.features_locked = 0;
+}
+
+long shstk_prctl(struct task_struct *task, int option, unsigned long features)
+{
+       if (option == ARCH_SHSTK_LOCK) {
+               task->thread.features_locked |= features;
+               return 0;
+       }
+
+       /* Don't allow via ptrace */
+       if (task != current)
+               return -EINVAL;
+
+       /* Do not allow to change locked features */
+       if (features & task->thread.features_locked)
+               return -EPERM;
+
+       /* Only support enabling/disabling one feature at a time. */
+       if (hweight_long(features) > 1)
+               return -EINVAL;
+
+       if (option == ARCH_SHSTK_DISABLE) {
+               return -EINVAL;
+       }
+
+       /* Handle ARCH_SHSTK_ENABLE */
+       return -EINVAL;
+}