forked from Minki/linux
Merge branch 'for-next/pac-set-get-enabled-keys' into for-next/core
* for-next/pac-set-get-enabled-keys: : Introduce arm64 prctl(PR_PAC_{SET,GET}_ENABLED_KEYS). arm64: pac: Optimize kernel entry/exit key installation code paths arm64: Introduce prctl(PR_PAC_{SET,GET}_ENABLED_KEYS) arm64: mte: make the per-task SCTLR_EL1 field usable elsewhere
This commit is contained in:
commit
a27a881656
@ -107,3 +107,37 @@ filter out the Pointer Authentication system key registers from
|
||||
KVM_GET/SET_REG_* ioctls and mask those features from cpufeature ID
|
||||
register. Any attempt to use the Pointer Authentication instructions will
|
||||
result in an UNDEFINED exception being injected into the guest.
|
||||
|
||||
|
||||
Enabling and disabling keys
|
||||
---------------------------
|
||||
|
||||
The prctl PR_PAC_SET_ENABLED_KEYS allows the user program to control which
|
||||
PAC keys are enabled in a particular task. It takes two arguments, the
|
||||
first being a bitmask of PR_PAC_APIAKEY, PR_PAC_APIBKEY, PR_PAC_APDAKEY
|
||||
and PR_PAC_APDBKEY specifying which keys shall be affected by this prctl,
|
||||
and the second being a bitmask of the same bits specifying whether the key
|
||||
should be enabled or disabled. For example::
|
||||
|
||||
prctl(PR_PAC_SET_ENABLED_KEYS,
|
||||
PR_PAC_APIAKEY | PR_PAC_APIBKEY | PR_PAC_APDAKEY | PR_PAC_APDBKEY,
|
||||
PR_PAC_APIBKEY, 0, 0);
|
||||
|
||||
disables all keys except the IB key.
|
||||
|
||||
The main reason why this is useful is to enable a userspace ABI that uses PAC
|
||||
instructions to sign and authenticate function pointers and other pointers
|
||||
exposed outside of the function, while still allowing binaries conforming to
|
||||
the ABI to interoperate with legacy binaries that do not sign or authenticate
|
||||
pointers.
|
||||
|
||||
The idea is that a dynamic loader or early startup code would issue this
|
||||
prctl very early after establishing that a process may load legacy binaries,
|
||||
but before executing any PAC instructions.
|
||||
|
||||
For compatibility with previous kernel versions, processes start up with IA,
|
||||
IB, DA and DB enabled, and are reset to this state on exec(). Processes created
|
||||
via fork() and clone() inherit the key enabled state from the calling process.
|
||||
|
||||
It is recommended to avoid disabling the IA key, as this has higher performance
|
||||
overhead than disabling any of the other keys.
|
||||
|
@ -13,30 +13,12 @@
|
||||
* so use the base value of ldp as thread.keys_user and offset as
|
||||
* thread.keys_user.ap*.
|
||||
*/
|
||||
.macro ptrauth_keys_install_user tsk, tmp1, tmp2, tmp3
|
||||
.macro __ptrauth_keys_install_user tsk, tmp1, tmp2, tmp3
|
||||
mov \tmp1, #THREAD_KEYS_USER
|
||||
add \tmp1, \tsk, \tmp1
|
||||
alternative_if_not ARM64_HAS_ADDRESS_AUTH
|
||||
b .Laddr_auth_skip_\@
|
||||
alternative_else_nop_endif
|
||||
ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_USER_KEY_APIA]
|
||||
msr_s SYS_APIAKEYLO_EL1, \tmp2
|
||||
msr_s SYS_APIAKEYHI_EL1, \tmp3
|
||||
ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_USER_KEY_APIB]
|
||||
msr_s SYS_APIBKEYLO_EL1, \tmp2
|
||||
msr_s SYS_APIBKEYHI_EL1, \tmp3
|
||||
ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_USER_KEY_APDA]
|
||||
msr_s SYS_APDAKEYLO_EL1, \tmp2
|
||||
msr_s SYS_APDAKEYHI_EL1, \tmp3
|
||||
ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_USER_KEY_APDB]
|
||||
msr_s SYS_APDBKEYLO_EL1, \tmp2
|
||||
msr_s SYS_APDBKEYHI_EL1, \tmp3
|
||||
.Laddr_auth_skip_\@:
|
||||
alternative_if ARM64_HAS_GENERIC_AUTH
|
||||
ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_USER_KEY_APGA]
|
||||
msr_s SYS_APGAKEYLO_EL1, \tmp2
|
||||
msr_s SYS_APGAKEYHI_EL1, \tmp3
|
||||
alternative_else_nop_endif
|
||||
.endm
|
||||
|
||||
.macro __ptrauth_keys_install_kernel_nosync tsk, tmp1, tmp2, tmp3
|
||||
|
@ -39,7 +39,7 @@ void mte_free_tag_storage(char *storage);
|
||||
|
||||
void mte_sync_tags(pte_t *ptep, pte_t pte);
|
||||
void mte_copy_page_tags(void *kto, const void *kfrom);
|
||||
void flush_mte_state(void);
|
||||
void mte_thread_init_user(void);
|
||||
void mte_thread_switch(struct task_struct *next);
|
||||
void mte_suspend_enter(void);
|
||||
void mte_suspend_exit(void);
|
||||
@ -59,7 +59,7 @@ static inline void mte_sync_tags(pte_t *ptep, pte_t pte)
|
||||
static inline void mte_copy_page_tags(void *kto, const void *kfrom)
|
||||
{
|
||||
}
|
||||
static inline void flush_mte_state(void)
|
||||
static inline void mte_thread_init_user(void)
|
||||
{
|
||||
}
|
||||
static inline void mte_thread_switch(struct task_struct *next)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define __ASM_POINTER_AUTH_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/prctl.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#include <asm/cpufeature.h>
|
||||
@ -34,6 +35,25 @@ struct ptrauth_keys_kernel {
|
||||
struct ptrauth_key apia;
|
||||
};
|
||||
|
||||
#define __ptrauth_key_install_nosync(k, v) \
|
||||
do { \
|
||||
struct ptrauth_key __pki_v = (v); \
|
||||
write_sysreg_s(__pki_v.lo, SYS_ ## k ## KEYLO_EL1); \
|
||||
write_sysreg_s(__pki_v.hi, SYS_ ## k ## KEYHI_EL1); \
|
||||
} while (0)
|
||||
|
||||
static inline void ptrauth_keys_install_user(struct ptrauth_keys_user *keys)
|
||||
{
|
||||
if (system_supports_address_auth()) {
|
||||
__ptrauth_key_install_nosync(APIB, keys->apib);
|
||||
__ptrauth_key_install_nosync(APDA, keys->apda);
|
||||
__ptrauth_key_install_nosync(APDB, keys->apdb);
|
||||
}
|
||||
|
||||
if (system_supports_generic_auth())
|
||||
__ptrauth_key_install_nosync(APGA, keys->apga);
|
||||
}
|
||||
|
||||
static inline void ptrauth_keys_init_user(struct ptrauth_keys_user *keys)
|
||||
{
|
||||
if (system_supports_address_auth()) {
|
||||
@ -45,14 +65,9 @@ static inline void ptrauth_keys_init_user(struct ptrauth_keys_user *keys)
|
||||
|
||||
if (system_supports_generic_auth())
|
||||
get_random_bytes(&keys->apga, sizeof(keys->apga));
|
||||
}
|
||||
|
||||
#define __ptrauth_key_install_nosync(k, v) \
|
||||
do { \
|
||||
struct ptrauth_key __pki_v = (v); \
|
||||
write_sysreg_s(__pki_v.lo, SYS_ ## k ## KEYLO_EL1); \
|
||||
write_sysreg_s(__pki_v.hi, SYS_ ## k ## KEYHI_EL1); \
|
||||
} while (0)
|
||||
ptrauth_keys_install_user(keys);
|
||||
}
|
||||
|
||||
static __always_inline void ptrauth_keys_init_kernel(struct ptrauth_keys_kernel *keys)
|
||||
{
|
||||
@ -71,6 +86,10 @@ static __always_inline void ptrauth_keys_switch_kernel(struct ptrauth_keys_kerne
|
||||
|
||||
extern int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg);
|
||||
|
||||
extern int ptrauth_set_enabled_keys(struct task_struct *tsk, unsigned long keys,
|
||||
unsigned long enabled);
|
||||
extern int ptrauth_get_enabled_keys(struct task_struct *tsk);
|
||||
|
||||
static inline unsigned long ptrauth_strip_insn_pac(unsigned long ptr)
|
||||
{
|
||||
return ptrauth_clear_pac(ptr);
|
||||
@ -85,8 +104,23 @@ static __always_inline void ptrauth_enable(void)
|
||||
isb();
|
||||
}
|
||||
|
||||
#define ptrauth_thread_init_user(tsk) \
|
||||
ptrauth_keys_init_user(&(tsk)->thread.keys_user)
|
||||
#define ptrauth_suspend_exit() \
|
||||
ptrauth_keys_install_user(¤t->thread.keys_user)
|
||||
|
||||
#define ptrauth_thread_init_user() \
|
||||
do { \
|
||||
ptrauth_keys_init_user(¤t->thread.keys_user); \
|
||||
\
|
||||
/* enable all keys */ \
|
||||
if (system_supports_address_auth()) \
|
||||
set_task_sctlr_el1(current->thread.sctlr_user | \
|
||||
SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | \
|
||||
SCTLR_ELx_ENDA | SCTLR_ELx_ENDB); \
|
||||
} while (0)
|
||||
|
||||
#define ptrauth_thread_switch_user(tsk) \
|
||||
ptrauth_keys_install_user(&(tsk)->thread.keys_user)
|
||||
|
||||
#define ptrauth_thread_init_kernel(tsk) \
|
||||
ptrauth_keys_init_kernel(&(tsk)->thread.keys_kernel)
|
||||
#define ptrauth_thread_switch_kernel(tsk) \
|
||||
@ -95,10 +129,17 @@ static __always_inline void ptrauth_enable(void)
|
||||
#else /* CONFIG_ARM64_PTR_AUTH */
|
||||
#define ptrauth_enable()
|
||||
#define ptrauth_prctl_reset_keys(tsk, arg) (-EINVAL)
|
||||
#define ptrauth_set_enabled_keys(tsk, keys, enabled) (-EINVAL)
|
||||
#define ptrauth_get_enabled_keys(tsk) (-EINVAL)
|
||||
#define ptrauth_strip_insn_pac(lr) (lr)
|
||||
#define ptrauth_thread_init_user(tsk)
|
||||
#define ptrauth_suspend_exit()
|
||||
#define ptrauth_thread_init_user()
|
||||
#define ptrauth_thread_init_kernel(tsk)
|
||||
#define ptrauth_thread_switch_user(tsk)
|
||||
#define ptrauth_thread_switch_kernel(tsk)
|
||||
#endif /* CONFIG_ARM64_PTR_AUTH */
|
||||
|
||||
#define PR_PAC_ENABLED_KEYS_MASK \
|
||||
(PR_PAC_APIAKEY | PR_PAC_APIBKEY | PR_PAC_APDAKEY | PR_PAC_APDBKEY)
|
||||
|
||||
#endif /* __ASM_POINTER_AUTH_H */
|
||||
|
@ -151,11 +151,15 @@ struct thread_struct {
|
||||
struct ptrauth_keys_kernel keys_kernel;
|
||||
#endif
|
||||
#ifdef CONFIG_ARM64_MTE
|
||||
u64 sctlr_tcf0;
|
||||
u64 gcr_user_excl;
|
||||
#endif
|
||||
u64 sctlr_user;
|
||||
};
|
||||
|
||||
#define SCTLR_USER_MASK \
|
||||
(SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | SCTLR_ELx_ENDA | SCTLR_ELx_ENDB | \
|
||||
SCTLR_EL1_TCF0_MASK)
|
||||
|
||||
static inline void arch_thread_struct_whitelist(unsigned long *offset,
|
||||
unsigned long *size)
|
||||
{
|
||||
@ -247,6 +251,8 @@ extern void release_thread(struct task_struct *);
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p);
|
||||
|
||||
void set_task_sctlr_el1(u64 sctlr);
|
||||
|
||||
/* Thread switching */
|
||||
extern struct task_struct *cpu_switch_to(struct task_struct *prev,
|
||||
struct task_struct *next);
|
||||
@ -301,6 +307,11 @@ extern void __init minsigstksz_setup(void);
|
||||
/* PR_PAC_RESET_KEYS prctl */
|
||||
#define PAC_RESET_KEYS(tsk, arg) ptrauth_prctl_reset_keys(tsk, arg)
|
||||
|
||||
/* PR_PAC_{SET,GET}_ENABLED_KEYS prctl */
|
||||
#define PAC_SET_ENABLED_KEYS(tsk, keys, enabled) \
|
||||
ptrauth_set_enabled_keys(tsk, keys, enabled)
|
||||
#define PAC_GET_ENABLED_KEYS(tsk) ptrauth_get_enabled_keys(tsk)
|
||||
|
||||
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
|
||||
/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
|
||||
long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg);
|
||||
|
@ -571,8 +571,10 @@
|
||||
#define SCTLR_ELx_TCF_ASYNC (UL(0x2) << SCTLR_ELx_TCF_SHIFT)
|
||||
#define SCTLR_ELx_TCF_MASK (UL(0x3) << SCTLR_ELx_TCF_SHIFT)
|
||||
|
||||
#define SCTLR_ELx_ENIA_SHIFT 31
|
||||
|
||||
#define SCTLR_ELx_ITFSB (BIT(37))
|
||||
#define SCTLR_ELx_ENIA (BIT(31))
|
||||
#define SCTLR_ELx_ENIA (BIT(SCTLR_ELx_ENIA_SHIFT))
|
||||
#define SCTLR_ELx_ENIB (BIT(30))
|
||||
#define SCTLR_ELx_ENDA (BIT(27))
|
||||
#define SCTLR_ELx_EE (BIT(25))
|
||||
|
@ -43,6 +43,7 @@ int main(void)
|
||||
#endif
|
||||
BLANK();
|
||||
DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context));
|
||||
DEFINE(THREAD_SCTLR_USER, offsetof(struct task_struct, thread.sctlr_user));
|
||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||
DEFINE(THREAD_KEYS_USER, offsetof(struct task_struct, thread.keys_user));
|
||||
DEFINE(THREAD_KEYS_KERNEL, offsetof(struct task_struct, thread.keys_kernel));
|
||||
@ -149,10 +150,6 @@ int main(void)
|
||||
#endif
|
||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||
DEFINE(PTRAUTH_USER_KEY_APIA, offsetof(struct ptrauth_keys_user, apia));
|
||||
DEFINE(PTRAUTH_USER_KEY_APIB, offsetof(struct ptrauth_keys_user, apib));
|
||||
DEFINE(PTRAUTH_USER_KEY_APDA, offsetof(struct ptrauth_keys_user, apda));
|
||||
DEFINE(PTRAUTH_USER_KEY_APDB, offsetof(struct ptrauth_keys_user, apdb));
|
||||
DEFINE(PTRAUTH_USER_KEY_APGA, offsetof(struct ptrauth_keys_user, apga));
|
||||
DEFINE(PTRAUTH_KERNEL_KEY_APIA, offsetof(struct ptrauth_keys_kernel, apia));
|
||||
BLANK();
|
||||
#endif
|
||||
|
@ -247,7 +247,29 @@ alternative_else_nop_endif
|
||||
check_mte_async_tcf x19, x22
|
||||
apply_ssbd 1, x22, x23
|
||||
|
||||
ptrauth_keys_install_kernel tsk, x20, x22, x23
|
||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||
alternative_if ARM64_HAS_ADDRESS_AUTH
|
||||
/*
|
||||
* Enable IA for in-kernel PAC if the task had it disabled. Although
|
||||
* this could be implemented with an unconditional MRS which would avoid
|
||||
* a load, this was measured to be slower on Cortex-A75 and Cortex-A76.
|
||||
*
|
||||
* Install the kernel IA key only if IA was enabled in the task. If IA
|
||||
* was disabled on kernel exit then we would have left the kernel IA
|
||||
* installed so there is no need to install it again.
|
||||
*/
|
||||
ldr x0, [tsk, THREAD_SCTLR_USER]
|
||||
tbz x0, SCTLR_ELx_ENIA_SHIFT, 1f
|
||||
__ptrauth_keys_install_kernel_nosync tsk, x20, x22, x23
|
||||
b 2f
|
||||
1:
|
||||
mrs x0, sctlr_el1
|
||||
orr x0, x0, SCTLR_ELx_ENIA
|
||||
msr sctlr_el1, x0
|
||||
2:
|
||||
isb
|
||||
alternative_else_nop_endif
|
||||
#endif
|
||||
|
||||
mte_set_kernel_gcr x22, x23
|
||||
|
||||
@ -351,8 +373,26 @@ alternative_else_nop_endif
|
||||
3:
|
||||
scs_save tsk, x0
|
||||
|
||||
/* No kernel C function calls after this as user keys are set. */
|
||||
ptrauth_keys_install_user tsk, x0, x1, x2
|
||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||
alternative_if ARM64_HAS_ADDRESS_AUTH
|
||||
/*
|
||||
* IA was enabled for in-kernel PAC. Disable it now if needed, or
|
||||
* alternatively install the user's IA. All other per-task keys and
|
||||
* SCTLR bits were updated on task switch.
|
||||
*
|
||||
* No kernel C function calls after this.
|
||||
*/
|
||||
ldr x0, [tsk, THREAD_SCTLR_USER]
|
||||
tbz x0, SCTLR_ELx_ENIA_SHIFT, 1f
|
||||
__ptrauth_keys_install_user tsk, x0, x1, x2
|
||||
b 2f
|
||||
1:
|
||||
mrs x0, sctlr_el1
|
||||
bic x0, x0, SCTLR_ELx_ENIA
|
||||
msr sctlr_el1, x0
|
||||
2:
|
||||
alternative_else_nop_endif
|
||||
#endif
|
||||
|
||||
mte_set_user_gcr tsk, x0, x1
|
||||
|
||||
|
@ -185,26 +185,6 @@ void mte_check_tfsr_el1(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void update_sctlr_el1_tcf0(u64 tcf0)
|
||||
{
|
||||
/* ISB required for the kernel uaccess routines */
|
||||
sysreg_clear_set(sctlr_el1, SCTLR_EL1_TCF0_MASK, tcf0);
|
||||
isb();
|
||||
}
|
||||
|
||||
static void set_sctlr_el1_tcf0(u64 tcf0)
|
||||
{
|
||||
/*
|
||||
* mte_thread_switch() checks current->thread.sctlr_tcf0 as an
|
||||
* optimisation. Disable preemption so that it does not see
|
||||
* the variable update before the SCTLR_EL1.TCF0 one.
|
||||
*/
|
||||
preempt_disable();
|
||||
current->thread.sctlr_tcf0 = tcf0;
|
||||
update_sctlr_el1_tcf0(tcf0);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static void update_gcr_el1_excl(u64 excl)
|
||||
{
|
||||
|
||||
@ -227,7 +207,7 @@ static void set_gcr_el1_excl(u64 excl)
|
||||
*/
|
||||
}
|
||||
|
||||
void flush_mte_state(void)
|
||||
void mte_thread_init_user(void)
|
||||
{
|
||||
if (!system_supports_mte())
|
||||
return;
|
||||
@ -237,31 +217,22 @@ void flush_mte_state(void)
|
||||
write_sysreg_s(0, SYS_TFSRE0_EL1);
|
||||
clear_thread_flag(TIF_MTE_ASYNC_FAULT);
|
||||
/* disable tag checking */
|
||||
set_sctlr_el1_tcf0(SCTLR_EL1_TCF0_NONE);
|
||||
set_task_sctlr_el1((current->thread.sctlr_user & ~SCTLR_EL1_TCF0_MASK) |
|
||||
SCTLR_EL1_TCF0_NONE);
|
||||
/* reset tag generation mask */
|
||||
set_gcr_el1_excl(SYS_GCR_EL1_EXCL_MASK);
|
||||
}
|
||||
|
||||
void mte_thread_switch(struct task_struct *next)
|
||||
{
|
||||
if (!system_supports_mte())
|
||||
return;
|
||||
|
||||
/* avoid expensive SCTLR_EL1 accesses if no change */
|
||||
if (current->thread.sctlr_tcf0 != next->thread.sctlr_tcf0)
|
||||
update_sctlr_el1_tcf0(next->thread.sctlr_tcf0);
|
||||
else
|
||||
isb();
|
||||
|
||||
/*
|
||||
* Check if an async tag exception occurred at EL1.
|
||||
*
|
||||
* Note: On the context switch path we rely on the dsb() present
|
||||
* in __switch_to() to guarantee that the indirect writes to TFSR_EL1
|
||||
* are synchronized before this point.
|
||||
* isb() above is required for the same reason.
|
||||
*
|
||||
*/
|
||||
isb();
|
||||
mte_check_tfsr_el1();
|
||||
}
|
||||
|
||||
@ -291,7 +262,7 @@ void mte_suspend_exit(void)
|
||||
|
||||
long set_mte_ctrl(struct task_struct *task, unsigned long arg)
|
||||
{
|
||||
u64 tcf0;
|
||||
u64 sctlr = task->thread.sctlr_user & ~SCTLR_EL1_TCF0_MASK;
|
||||
u64 gcr_excl = ~((arg & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT) &
|
||||
SYS_GCR_EL1_EXCL_MASK;
|
||||
|
||||
@ -300,23 +271,23 @@ long set_mte_ctrl(struct task_struct *task, unsigned long arg)
|
||||
|
||||
switch (arg & PR_MTE_TCF_MASK) {
|
||||
case PR_MTE_TCF_NONE:
|
||||
tcf0 = SCTLR_EL1_TCF0_NONE;
|
||||
sctlr |= SCTLR_EL1_TCF0_NONE;
|
||||
break;
|
||||
case PR_MTE_TCF_SYNC:
|
||||
tcf0 = SCTLR_EL1_TCF0_SYNC;
|
||||
sctlr |= SCTLR_EL1_TCF0_SYNC;
|
||||
break;
|
||||
case PR_MTE_TCF_ASYNC:
|
||||
tcf0 = SCTLR_EL1_TCF0_ASYNC;
|
||||
sctlr |= SCTLR_EL1_TCF0_ASYNC;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (task != current) {
|
||||
task->thread.sctlr_tcf0 = tcf0;
|
||||
task->thread.sctlr_user = sctlr;
|
||||
task->thread.gcr_user_excl = gcr_excl;
|
||||
} else {
|
||||
set_sctlr_el1_tcf0(tcf0);
|
||||
set_task_sctlr_el1(sctlr);
|
||||
set_gcr_el1_excl(gcr_excl);
|
||||
}
|
||||
|
||||
@ -333,7 +304,7 @@ long get_mte_ctrl(struct task_struct *task)
|
||||
|
||||
ret = incl << PR_MTE_TAG_SHIFT;
|
||||
|
||||
switch (task->thread.sctlr_tcf0) {
|
||||
switch (task->thread.sctlr_user & SCTLR_EL1_TCF0_MASK) {
|
||||
case SCTLR_EL1_TCF0_NONE:
|
||||
ret |= PR_MTE_TCF_NONE;
|
||||
break;
|
||||
|
@ -43,6 +43,69 @@ int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg)
|
||||
get_random_bytes(&keys->apdb, sizeof(keys->apdb));
|
||||
if (arg & PR_PAC_APGAKEY)
|
||||
get_random_bytes(&keys->apga, sizeof(keys->apga));
|
||||
ptrauth_keys_install_user(keys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 arg_to_enxx_mask(unsigned long arg)
|
||||
{
|
||||
u64 sctlr_enxx_mask = 0;
|
||||
|
||||
WARN_ON(arg & ~PR_PAC_ENABLED_KEYS_MASK);
|
||||
if (arg & PR_PAC_APIAKEY)
|
||||
sctlr_enxx_mask |= SCTLR_ELx_ENIA;
|
||||
if (arg & PR_PAC_APIBKEY)
|
||||
sctlr_enxx_mask |= SCTLR_ELx_ENIB;
|
||||
if (arg & PR_PAC_APDAKEY)
|
||||
sctlr_enxx_mask |= SCTLR_ELx_ENDA;
|
||||
if (arg & PR_PAC_APDBKEY)
|
||||
sctlr_enxx_mask |= SCTLR_ELx_ENDB;
|
||||
return sctlr_enxx_mask;
|
||||
}
|
||||
|
||||
int ptrauth_set_enabled_keys(struct task_struct *tsk, unsigned long keys,
|
||||
unsigned long enabled)
|
||||
{
|
||||
u64 sctlr = tsk->thread.sctlr_user;
|
||||
|
||||
if (!system_supports_address_auth())
|
||||
return -EINVAL;
|
||||
|
||||
if (is_compat_thread(task_thread_info(tsk)))
|
||||
return -EINVAL;
|
||||
|
||||
if ((keys & ~PR_PAC_ENABLED_KEYS_MASK) || (enabled & ~keys))
|
||||
return -EINVAL;
|
||||
|
||||
sctlr &= ~arg_to_enxx_mask(keys);
|
||||
sctlr |= arg_to_enxx_mask(enabled);
|
||||
if (tsk == current)
|
||||
set_task_sctlr_el1(sctlr);
|
||||
else
|
||||
tsk->thread.sctlr_user = sctlr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ptrauth_get_enabled_keys(struct task_struct *tsk)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (!system_supports_address_auth())
|
||||
return -EINVAL;
|
||||
|
||||
if (is_compat_thread(task_thread_info(tsk)))
|
||||
return -EINVAL;
|
||||
|
||||
if (tsk->thread.sctlr_user & SCTLR_ELx_ENIA)
|
||||
retval |= PR_PAC_APIAKEY;
|
||||
if (tsk->thread.sctlr_user & SCTLR_ELx_ENIB)
|
||||
retval |= PR_PAC_APIBKEY;
|
||||
if (tsk->thread.sctlr_user & SCTLR_ELx_ENDA)
|
||||
retval |= PR_PAC_APDAKEY;
|
||||
if (tsk->thread.sctlr_user & SCTLR_ELx_ENDB)
|
||||
retval |= PR_PAC_APDBKEY;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -339,7 +339,6 @@ void flush_thread(void)
|
||||
tls_thread_flush();
|
||||
flush_ptrace_hw_breakpoint(current);
|
||||
flush_tagged_addr_state();
|
||||
flush_mte_state();
|
||||
}
|
||||
|
||||
void release_thread(struct task_struct *dead_task)
|
||||
@ -529,6 +528,31 @@ static void erratum_1418040_thread_switch(struct task_struct *prev,
|
||||
write_sysreg(val, cntkctl_el1);
|
||||
}
|
||||
|
||||
static void update_sctlr_el1(u64 sctlr)
|
||||
{
|
||||
/*
|
||||
* EnIA must not be cleared while in the kernel as this is necessary for
|
||||
* in-kernel PAC. It will be cleared on kernel exit if needed.
|
||||
*/
|
||||
sysreg_clear_set(sctlr_el1, SCTLR_USER_MASK & ~SCTLR_ELx_ENIA, sctlr);
|
||||
|
||||
/* ISB required for the kernel uaccess routines when setting TCF0. */
|
||||
isb();
|
||||
}
|
||||
|
||||
void set_task_sctlr_el1(u64 sctlr)
|
||||
{
|
||||
/*
|
||||
* __switch_to() checks current->thread.sctlr as an
|
||||
* optimisation. Disable preemption so that it does not see
|
||||
* the variable update before the SCTLR_EL1 one.
|
||||
*/
|
||||
preempt_disable();
|
||||
current->thread.sctlr_user = sctlr;
|
||||
update_sctlr_el1(sctlr);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Thread switching.
|
||||
*/
|
||||
@ -544,6 +568,7 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
|
||||
entry_task_switch(next);
|
||||
ssbs_thread_switch(next);
|
||||
erratum_1418040_thread_switch(prev, next);
|
||||
ptrauth_thread_switch_user(next);
|
||||
|
||||
/*
|
||||
* Complete any pending TLB or cache maintenance on this CPU in case
|
||||
@ -559,6 +584,9 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
|
||||
* registers.
|
||||
*/
|
||||
mte_thread_switch(next);
|
||||
/* avoid expensive SCTLR_EL1 accesses if no change */
|
||||
if (prev->thread.sctlr_user != next->thread.sctlr_user)
|
||||
update_sctlr_el1(next->thread.sctlr_user);
|
||||
|
||||
/* the actual thread switch */
|
||||
last = cpu_switch_to(prev, next);
|
||||
@ -608,7 +636,8 @@ void arch_setup_new_exec(void)
|
||||
{
|
||||
current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
|
||||
|
||||
ptrauth_thread_init_user(current);
|
||||
ptrauth_thread_init_user();
|
||||
mte_thread_init_user();
|
||||
|
||||
if (task_spec_ssb_noexec(current)) {
|
||||
arch_prctl_spec_ctrl_set(current, PR_SPEC_STORE_BYPASS,
|
||||
|
@ -909,6 +909,38 @@ static int pac_mask_get(struct task_struct *target,
|
||||
return membuf_write(&to, &uregs, sizeof(uregs));
|
||||
}
|
||||
|
||||
static int pac_enabled_keys_get(struct task_struct *target,
|
||||
const struct user_regset *regset,
|
||||
struct membuf to)
|
||||
{
|
||||
long enabled_keys = ptrauth_get_enabled_keys(target);
|
||||
|
||||
if (IS_ERR_VALUE(enabled_keys))
|
||||
return enabled_keys;
|
||||
|
||||
return membuf_write(&to, &enabled_keys, sizeof(enabled_keys));
|
||||
}
|
||||
|
||||
static int pac_enabled_keys_set(struct task_struct *target,
|
||||
const struct user_regset *regset,
|
||||
unsigned int pos, unsigned int count,
|
||||
const void *kbuf, const void __user *ubuf)
|
||||
{
|
||||
int ret;
|
||||
long enabled_keys = ptrauth_get_enabled_keys(target);
|
||||
|
||||
if (IS_ERR_VALUE(enabled_keys))
|
||||
return enabled_keys;
|
||||
|
||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &enabled_keys, 0,
|
||||
sizeof(long));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ptrauth_set_enabled_keys(target, PR_PAC_ENABLED_KEYS_MASK,
|
||||
enabled_keys);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
static __uint128_t pac_key_to_user(const struct ptrauth_key *key)
|
||||
{
|
||||
@ -1074,6 +1106,7 @@ enum aarch64_regset {
|
||||
#endif
|
||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||
REGSET_PAC_MASK,
|
||||
REGSET_PAC_ENABLED_KEYS,
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
REGSET_PACA_KEYS,
|
||||
REGSET_PACG_KEYS,
|
||||
@ -1160,6 +1193,14 @@ static const struct user_regset aarch64_regsets[] = {
|
||||
.regset_get = pac_mask_get,
|
||||
/* this cannot be set dynamically */
|
||||
},
|
||||
[REGSET_PAC_ENABLED_KEYS] = {
|
||||
.core_note_type = NT_ARM_PAC_ENABLED_KEYS,
|
||||
.n = 1,
|
||||
.size = sizeof(long),
|
||||
.align = sizeof(long),
|
||||
.regset_get = pac_enabled_keys_get,
|
||||
.set = pac_enabled_keys_set,
|
||||
},
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
[REGSET_PACA_KEYS] = {
|
||||
.core_note_type = NT_ARM_PACA_KEYS,
|
||||
|
@ -74,8 +74,9 @@ void notrace __cpu_suspend_exit(void)
|
||||
*/
|
||||
spectre_v4_enable_mitigation(NULL);
|
||||
|
||||
/* Restore additional MTE-specific configuration */
|
||||
/* Restore additional feature-specific configuration */
|
||||
mte_suspend_exit();
|
||||
ptrauth_suspend_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -426,6 +426,7 @@ typedef struct elf64_shdr {
|
||||
#define NT_ARM_PACA_KEYS 0x407 /* ARM pointer authentication address keys */
|
||||
#define NT_ARM_PACG_KEYS 0x408 /* ARM pointer authentication generic key */
|
||||
#define NT_ARM_TAGGED_ADDR_CTRL 0x409 /* arm64 tagged address control (prctl()) */
|
||||
#define NT_ARM_PAC_ENABLED_KEYS 0x40a /* arm64 ptr auth enabled keys (prctl()) */
|
||||
#define NT_ARC_V2 0x600 /* ARCv2 accumulator/extra registers */
|
||||
#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note */
|
||||
#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */
|
||||
|
@ -255,4 +255,8 @@ struct prctl_mm_map {
|
||||
# define SYSCALL_DISPATCH_FILTER_ALLOW 0
|
||||
# define SYSCALL_DISPATCH_FILTER_BLOCK 1
|
||||
|
||||
/* Set/get enabled arm64 pointer authentication keys */
|
||||
#define PR_PAC_SET_ENABLED_KEYS 60
|
||||
#define PR_PAC_GET_ENABLED_KEYS 61
|
||||
|
||||
#endif /* _LINUX_PRCTL_H */
|
||||
|
16
kernel/sys.c
16
kernel/sys.c
@ -119,6 +119,12 @@
|
||||
#ifndef PAC_RESET_KEYS
|
||||
# define PAC_RESET_KEYS(a, b) (-EINVAL)
|
||||
#endif
|
||||
#ifndef PAC_SET_ENABLED_KEYS
|
||||
# define PAC_SET_ENABLED_KEYS(a, b, c) (-EINVAL)
|
||||
#endif
|
||||
#ifndef PAC_GET_ENABLED_KEYS
|
||||
# define PAC_GET_ENABLED_KEYS(a) (-EINVAL)
|
||||
#endif
|
||||
#ifndef SET_TAGGED_ADDR_CTRL
|
||||
# define SET_TAGGED_ADDR_CTRL(a) (-EINVAL)
|
||||
#endif
|
||||
@ -2497,6 +2503,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
return -EINVAL;
|
||||
error = PAC_RESET_KEYS(me, arg2);
|
||||
break;
|
||||
case PR_PAC_SET_ENABLED_KEYS:
|
||||
if (arg4 || arg5)
|
||||
return -EINVAL;
|
||||
error = PAC_SET_ENABLED_KEYS(me, arg2, arg3);
|
||||
break;
|
||||
case PR_PAC_GET_ENABLED_KEYS:
|
||||
if (arg2 || arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
error = PAC_GET_ENABLED_KEYS(me);
|
||||
break;
|
||||
case PR_SET_TAGGED_ADDR_CTRL:
|
||||
if (arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
|
Loading…
Reference in New Issue
Block a user