arm64: add prctl control for resetting ptrauth keys
authorKristina Martsenko <kristina.martsenko@arm.com>
Fri, 7 Dec 2018 18:39:28 +0000 (18:39 +0000)
committerWill Deacon <will.deacon@arm.com>
Thu, 13 Dec 2018 16:42:46 +0000 (16:42 +0000)
Add an arm64-specific prctl to allow a thread to reinitialize its
pointer authentication keys to random values. This can be useful when
exec() is not used for starting new processes, to ensure that different
processes still have different keys.

Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
arch/arm64/include/asm/pointer_auth.h
arch/arm64/include/asm/processor.h
arch/arm64/kernel/Makefile
arch/arm64/kernel/pointer_auth.c [new file with mode: 0644]
include/uapi/linux/prctl.h
kernel/sys.c

index 5ccf49b4dac38ff4decf900e2a1213ed694bd65a..80eb03afd6771c6a863711df5f8de3dbd6c98378 100644 (file)
@@ -63,6 +63,8 @@ static inline void ptrauth_keys_switch(struct ptrauth_keys *keys)
                __ptrauth_key_install(APGA, keys->apga);
 }
 
+extern int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg);
+
 /*
  * The EL0 pointer bits used by a pointer authentication code.
  * This is dependent on TBI0 being enabled, or bits 63:56 would also apply.
@@ -86,6 +88,7 @@ do {                                                                  \
        ptrauth_keys_switch(&(tsk)->thread_info.keys_user)
 
 #else /* CONFIG_ARM64_PTR_AUTH */
+#define ptrauth_prctl_reset_keys(tsk, arg)     (-EINVAL)
 #define ptrauth_strip_insn_pac(lr)     (lr)
 #define ptrauth_thread_init_user(tsk)
 #define ptrauth_thread_switch(tsk)
index f4b8e09aff564086da163dca2b9403615b6367cb..142c708cb429eed6c9f2ba7d6d7e83a0289595d2 100644 (file)
@@ -44,6 +44,7 @@
 #include <asm/hw_breakpoint.h>
 #include <asm/lse.h>
 #include <asm/pgtable-hwdef.h>
+#include <asm/pointer_auth.h>
 #include <asm/ptrace.h>
 #include <asm/types.h>
 
@@ -289,6 +290,9 @@ extern void __init minsigstksz_setup(void);
 #define SVE_SET_VL(arg)        sve_set_current_vl(arg)
 #define SVE_GET_VL()   sve_get_current_vl()
 
+/* PR_PAC_RESET_KEYS prctl */
+#define PAC_RESET_KEYS(tsk, arg)       ptrauth_prctl_reset_keys(tsk, arg)
+
 /*
  * For CONFIG_GCC_PLUGIN_STACKLEAK
  *
index 583334ce1c2c4134d3a08b2c1bce87d369b087f0..df08d735b21d37ef85b28064ed12a90ce99ad66b 100644 (file)
@@ -58,6 +58,7 @@ arm64-obj-$(CONFIG_CRASH_DUMP)                += crash_dump.o
 arm64-obj-$(CONFIG_CRASH_CORE)         += crash_core.o
 arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)  += sdei.o
 arm64-obj-$(CONFIG_ARM64_SSBD)         += ssbd.o
+arm64-obj-$(CONFIG_ARM64_PTR_AUTH)     += pointer_auth.o
 
 obj-y                                  += $(arm64-obj-y) vdso/ probes/
 obj-m                                  += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/pointer_auth.c b/arch/arm64/kernel/pointer_auth.c
new file mode 100644 (file)
index 0000000..b9f6f5f
--- /dev/null
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/errno.h>
+#include <linux/prctl.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <asm/cpufeature.h>
+#include <asm/pointer_auth.h>
+
+int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg)
+{
+       struct ptrauth_keys *keys = &tsk->thread_info.keys_user;
+       unsigned long addr_key_mask = PR_PAC_APIAKEY | PR_PAC_APIBKEY |
+                                     PR_PAC_APDAKEY | PR_PAC_APDBKEY;
+       unsigned long key_mask = addr_key_mask | PR_PAC_APGAKEY;
+
+       if (!system_supports_address_auth() && !system_supports_generic_auth())
+               return -EINVAL;
+
+       if (!arg) {
+               ptrauth_keys_init(keys);
+               ptrauth_keys_switch(keys);
+               return 0;
+       }
+
+       if (arg & ~key_mask)
+               return -EINVAL;
+
+       if (((arg & addr_key_mask) && !system_supports_address_auth()) ||
+           ((arg & PR_PAC_APGAKEY) && !system_supports_generic_auth()))
+               return -EINVAL;
+
+       if (arg & PR_PAC_APIAKEY)
+               get_random_bytes(&keys->apia, sizeof(keys->apia));
+       if (arg & PR_PAC_APIBKEY)
+               get_random_bytes(&keys->apib, sizeof(keys->apib));
+       if (arg & PR_PAC_APDAKEY)
+               get_random_bytes(&keys->apda, sizeof(keys->apda));
+       if (arg & PR_PAC_APDBKEY)
+               get_random_bytes(&keys->apdb, sizeof(keys->apdb));
+       if (arg & PR_PAC_APGAKEY)
+               get_random_bytes(&keys->apga, sizeof(keys->apga));
+
+       ptrauth_keys_switch(keys);
+
+       return 0;
+}
index c0d7ea0bf5b62438ca8184551b64d5d29ad7951b..0f535a5013916ce0c28998cc3e8fe318a5f82821 100644 (file)
@@ -219,4 +219,12 @@ struct prctl_mm_map {
 # define PR_SPEC_DISABLE               (1UL << 2)
 # define PR_SPEC_FORCE_DISABLE         (1UL << 3)
 
+/* Reset arm64 pointer authentication keys */
+#define PR_PAC_RESET_KEYS              54
+# define PR_PAC_APIAKEY                        (1UL << 0)
+# define PR_PAC_APIBKEY                        (1UL << 1)
+# define PR_PAC_APDAKEY                        (1UL << 2)
+# define PR_PAC_APDBKEY                        (1UL << 3)
+# define PR_PAC_APGAKEY                        (1UL << 4)
+
 #endif /* _LINUX_PRCTL_H */
index 123bd73046ec6b19324452f0b9563617d7e40630..64b5a230f38d785846ff69699bd74915a77a76d1 100644 (file)
 #ifndef SVE_GET_VL
 # define SVE_GET_VL()          (-EINVAL)
 #endif
+#ifndef PAC_RESET_KEYS
+# define PAC_RESET_KEYS(a, b)  (-EINVAL)
+#endif
 
 /*
  * this is where the system-wide overflow UID and GID are defined, for
@@ -2476,6 +2479,11 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                        return -EINVAL;
                error = arch_prctl_spec_ctrl_set(me, arg2, arg3);
                break;
+       case PR_PAC_RESET_KEYS:
+               if (arg3 || arg4 || arg5)
+                       return -EINVAL;
+               error = PAC_RESET_KEYS(me, arg2);
+               break;
        default:
                error = -EINVAL;
                break;