Hi Peter, Please keep libc-alpha in the loop for such ABI proposals (I'll bounce this patch separately). Thanks. On Wed, Nov 18, 2020 at 09:20:11PM -0800, Peter Collingbourne wrote: > This prctl allows the user program to control which PAC keys are enabled > in a particular task. The main reason why this is useful is to enable a > userspace ABI that uses PAC 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. > > Signed-off-by: Peter Collingbourne <pcc@xxxxxxxxxx> > Link: https://linux-review.googlesource.com/id/Ibc41a5e6a76b275efbaa126b31119dc197b927a5 > --- > v3: > - fix some style nits > - move kernel entry ISB to after setting EnIA > - rename sctlr -> sctlr_user > - remove init_sctlr > > v2: > - added prctl(PR_PAC_GET_ENABLED_KEYS) > - added ptrace APIs for getting and setting the set of enabled > keys > - optimized the instruction sequence for kernel entry/exit > - rebased on top of MTE series > > .../arm64/pointer-authentication.rst | 27 +++++++++ > arch/arm64/include/asm/mte.h | 4 +- > arch/arm64/include/asm/pointer_auth.h | 26 ++++++++- > arch/arm64/include/asm/processor.h | 19 ++++++- > arch/arm64/include/asm/sysreg.h | 4 +- > arch/arm64/kernel/asm-offsets.c | 1 + > arch/arm64/kernel/entry.S | 30 +++++++++- > arch/arm64/kernel/mte.c | 42 ++++---------- > arch/arm64/kernel/pointer_auth.c | 55 +++++++++++++++++++ > arch/arm64/kernel/process.c | 39 ++++++++++++- > arch/arm64/kernel/ptrace.c | 41 ++++++++++++++ > include/uapi/linux/elf.h | 1 + > include/uapi/linux/prctl.h | 4 ++ > kernel/sys.c | 16 ++++++ > 14 files changed, 267 insertions(+), 42 deletions(-) > > diff --git a/Documentation/arm64/pointer-authentication.rst b/Documentation/arm64/pointer-authentication.rst > index 30b2ab06526b..1f7e064deeb3 100644 > --- a/Documentation/arm64/pointer-authentication.rst > +++ b/Documentation/arm64/pointer-authentication.rst > @@ -107,3 +107,30 @@ 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. > diff --git a/arch/arm64/include/asm/mte.h b/arch/arm64/include/asm/mte.h > index 1c99fcadb58c..adbb05ece04c 100644 > --- a/arch/arm64/include/asm/mte.h > +++ b/arch/arm64/include/asm/mte.h > @@ -37,7 +37,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_exit(void); > long set_mte_ctrl(struct task_struct *task, unsigned long arg); > @@ -56,7 +56,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) > diff --git a/arch/arm64/include/asm/pointer_auth.h b/arch/arm64/include/asm/pointer_auth.h > index c6b4f0603024..8346f6e60736 100644 > --- a/arch/arm64/include/asm/pointer_auth.h > +++ b/arch/arm64/include/asm/pointer_auth.h > @@ -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> > @@ -71,13 +72,27 @@ 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_prctl_set_enabled_keys(struct task_struct *tsk, > + unsigned long keys, > + unsigned long enabled); > +extern int ptrauth_prctl_get_enabled_keys(struct task_struct *tsk); > + > static inline unsigned long ptrauth_strip_insn_pac(unsigned long ptr) > { > return ptrauth_clear_pac(ptr); > } > > -#define ptrauth_thread_init_user(tsk) \ > - ptrauth_keys_init_user(&(tsk)->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_init_kernel(tsk) \ > ptrauth_keys_init_kernel(&(tsk)->thread.keys_kernel) > #define ptrauth_thread_switch_kernel(tsk) \ > @@ -85,10 +100,15 @@ static inline unsigned long ptrauth_strip_insn_pac(unsigned long ptr) > > #else /* CONFIG_ARM64_PTR_AUTH */ > #define ptrauth_prctl_reset_keys(tsk, arg) (-EINVAL) > +#define ptrauth_prctl_set_enabled_keys(tsk, keys, enabled) (-EINVAL) > +#define ptrauth_prctl_get_enabled_keys(tsk) (-EINVAL) > #define ptrauth_strip_insn_pac(lr) (lr) > -#define ptrauth_thread_init_user(tsk) > +#define ptrauth_thread_init_user() > #define ptrauth_thread_init_kernel(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 */ > diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h > index fce8cbecd6bc..f430dd2fb61a 100644 > --- a/arch/arm64/include/asm/processor.h > +++ b/arch/arm64/include/asm/processor.h > @@ -153,11 +153,15 @@ struct thread_struct { > struct ptrauth_keys_kernel keys_kernel; > #endif > #ifdef CONFIG_ARM64_MTE > - u64 sctlr_tcf0; > u64 gcr_user_incl; > #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) > { > @@ -249,6 +253,14 @@ extern void release_thread(struct task_struct *); > > unsigned long get_wchan(struct task_struct *p); > > +#if defined(CONFIG_ARM64_PTR_AUTH) || defined(CONFIG_ARM64_MTE) > +void set_task_sctlr_el1(u64 sctlr); > +#else > +static inline void set_task_sctlr_el1(u64 sctlr) > +{ > +} > +#endif > + > /* Thread switching */ > extern struct task_struct *cpu_switch_to(struct task_struct *prev, > struct task_struct *next); > @@ -303,6 +315,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_prctl_set_enabled_keys(tsk, keys, enabled) > +#define PAC_GET_ENABLED_KEYS(tsk) ptrauth_prctl_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); > diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h > index e2ef4c2edf06..96e3337ca7b3 100644 > --- a/arch/arm64/include/asm/sysreg.h > +++ b/arch/arm64/include/asm/sysreg.h > @@ -554,8 +554,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)) > diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c > index 7d32fc959b1a..062d3e37edb5 100644 > --- a/arch/arm64/kernel/asm-offsets.c > +++ b/arch/arm64/kernel/asm-offsets.c > @@ -48,6 +48,7 @@ int main(void) > DEFINE(THREAD_KEYS_USER, offsetof(struct task_struct, thread.keys_user)); > DEFINE(THREAD_KEYS_KERNEL, offsetof(struct task_struct, thread.keys_kernel)); > #endif > + DEFINE(THREAD_SCTLR_USER, offsetof(struct task_struct, thread.sctlr_user)); > BLANK(); > DEFINE(S_X0, offsetof(struct pt_regs, regs[0])); > DEFINE(S_X2, offsetof(struct pt_regs, regs[2])); > diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S > index b295fb912b12..c8c1a284a76a 100644 > --- a/arch/arm64/kernel/entry.S > +++ b/arch/arm64/kernel/entry.S > @@ -210,7 +210,20 @@ alternative_else_nop_endif > check_mte_async_tcf x19, x22 > apply_ssbd 1, x22, x23 > > - ptrauth_keys_install_kernel tsk, x20, x22, x23 > + ptrauth_keys_install_kernel_nosync 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. */ > + ldr x0, [tsk, THREAD_SCTLR_USER] > + tbnz x0, SCTLR_ELx_ENIA_SHIFT, 1f > + mrs x0, sctlr_el1 > + orr x0, x0, SCTLR_ELx_ENIA > + msr sctlr_el1, x0 > +1: > + isb > +alternative_else_nop_endif > +#endif > > scs_load tsk, x20 > .else > @@ -330,6 +343,21 @@ alternative_else_nop_endif > /* 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. > + * All other per-task SCTLR bits were updated on task switch. > + */ > + ldr x0, [tsk, THREAD_SCTLR_USER] > + tbnz x0, SCTLR_ELx_ENIA_SHIFT, 1f > + mrs x0, sctlr_el1 > + bic x0, x0, SCTLR_ELx_ENIA > + msr sctlr_el1, x0 > +1: > +alternative_else_nop_endif > +#endif > + > apply_ssbd 0, x0, x1 > .endif > > diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c > index 52a0638ed967..16040a1e0fd1 100644 > --- a/arch/arm64/kernel/mte.c > +++ b/arch/arm64/kernel/mte.c > @@ -72,26 +72,6 @@ int memcmp_pages(struct page *page1, struct page *page2) > return ret; > } > > -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 incl) > { > u64 excl = ~incl & SYS_GCR_EL1_EXCL_MASK; > @@ -111,7 +91,7 @@ static void set_gcr_el1_excl(u64 incl) > update_gcr_el1_excl(incl); > } > > -void flush_mte_state(void) > +void mte_thread_init_user(void) > { > if (!system_supports_mte()) > return; > @@ -121,7 +101,8 @@ 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(0); > } > @@ -131,9 +112,6 @@ 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); > update_gcr_el1_excl(next->thread.gcr_user_incl); > } > > @@ -147,7 +125,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_incl = (arg & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT; > > if (!system_supports_mte()) > @@ -155,23 +133,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_incl = gcr_incl; > } else { > - set_sctlr_el1_tcf0(tcf0); > + set_task_sctlr_el1(sctlr); > set_gcr_el1_excl(gcr_incl); > } > > @@ -187,7 +165,7 @@ long get_mte_ctrl(struct task_struct *task) > > ret = task->thread.gcr_user_incl << PR_MTE_TAG_SHIFT; > > - switch (task->thread.sctlr_tcf0) { > + switch (task->thread.sctlr_user & SCTLR_EL1_TCF0_MASK) { > case SCTLR_EL1_TCF0_NONE: > return PR_MTE_TCF_NONE; > case SCTLR_EL1_TCF0_SYNC: > diff --git a/arch/arm64/kernel/pointer_auth.c b/arch/arm64/kernel/pointer_auth.c > index adb955fd9bdd..025f38dff464 100644 > --- a/arch/arm64/kernel/pointer_auth.c > +++ b/arch/arm64/kernel/pointer_auth.c > @@ -46,3 +46,58 @@ int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg) > > return 0; > } > + > +static u64 arg_to_enxx_mask(unsigned long arg) > +{ > + u64 sctlr_enxx_mask = 0; > + > + 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_prctl_set_enabled_keys(struct task_struct *tsk, unsigned long keys, > + unsigned long enabled) > +{ > + u64 sctlr = tsk->thread.sctlr_user; > + > + if (!system_supports_address_auth() || is_compat_task()) > + 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_prctl_get_enabled_keys(struct task_struct *tsk) > +{ > + int retval = 0; > + > + if (!system_supports_address_auth() || is_compat_task()) > + 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; > +} > diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c > index a47a40ec6ad9..aa8bf0294c5d 100644 > --- a/arch/arm64/kernel/process.c > +++ b/arch/arm64/kernel/process.c > @@ -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) > @@ -541,6 +540,37 @@ static void erratum_1418040_thread_switch(struct task_struct *prev, > write_sysreg(val, cntkctl_el1); > } > > +#if defined(CONFIG_ARM64_PTR_AUTH) || defined(CONFIG_ARM64_MTE) > +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(); > +} > +#else > +static void update_sctlr_el1(u64 sctlr) > +{ > +} > +#endif /* defined(CONFIG_ARM64_PTR_AUTH) || defined(CONFIG_ARM64_MTE) */ > + > /* > * Thread switching. > */ > @@ -566,6 +596,10 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev, > */ > dsb(ish); > > + /* avoid expensive SCTLR_EL1 accesses if no change */ > + if (prev->thread.sctlr_user != next->thread.sctlr_user) > + update_sctlr_el1(next->thread.sctlr_user); > + > /* > * MTE thread switching must happen after the DSB above to ensure that > * any asynchronous tag check faults have been logged in the TFSR*_EL1 > @@ -621,7 +655,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, > diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c > index f49b349e16a3..2ed17fb07666 100644 > --- a/arch/arm64/kernel/ptrace.c > +++ b/arch/arm64/kernel/ptrace.c > @@ -911,6 +911,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_prctl_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_prctl_get_enabled_keys(target); > + > + if (IS_ERR_VALUE(enabled_keys)) > + return enabled_keys; > + > + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, > + &enabled_keys, 0, -1); > + if (ret) > + return ret; > + > + return ptrauth_prctl_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) > { > @@ -1076,6 +1108,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, > @@ -1162,6 +1195,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, > diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h > index 30f68b42eeb5..61bf4774b8f2 100644 > --- a/include/uapi/linux/elf.h > +++ b/include/uapi/linux/elf.h > @@ -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 */ > diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h > index 7f0827705c9a..0d1bb3a2e59a 100644 > --- a/include/uapi/linux/prctl.h > +++ b/include/uapi/linux/prctl.h > @@ -247,4 +247,8 @@ struct prctl_mm_map { > #define PR_SET_IO_FLUSHER 57 > #define PR_GET_IO_FLUSHER 58 > > +/* Set/get enabled arm64 pointer authentication keys */ > +#define PR_PAC_SET_ENABLED_KEYS 59 > +#define PR_PAC_GET_ENABLED_KEYS 60 > + > #endif /* _LINUX_PRCTL_H */ > diff --git a/kernel/sys.c b/kernel/sys.c > index a730c03ee607..b7f2878f053b 100644 > --- a/kernel/sys.c > +++ b/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; > -- > 2.29.2.299.gdc1121823c-goog