mirror of https://gitee.com/openkylin/linux.git
arm64: Introduce prctl(PR_PAC_{SET,GET}_ENABLED_KEYS)
This change introduces a prctl that 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. This change adds a small amount of overhead to kernel entry and exit due to additional required instruction sequences. On a DragonBoard 845c (Cortex-A75) with the powersave governor, the overhead of similar instruction sequences was measured as 4.9ns when simulating the common case where IA is left enabled, or 43.7ns when simulating the uncommon case where IA is disabled. These numbers can be seen as the worst case scenario, since in more realistic scenarios a better performing governor would be used and a newer chip would be used that would support PAC unlike Cortex-A75 and would be expected to be faster than Cortex-A75. On an Apple M1 under a hypervisor, the overhead of the entry/exit instruction sequences introduced by this patch was measured as 0.3ns in the case where IA is left enabled, and 33.0ns in the case where IA is disabled. Signed-off-by: Peter Collingbourne <pcc@google.com> Reviewed-by: Dave Martin <Dave.Martin@arm.com> Link: https://linux-review.googlesource.com/id/Ibc41a5e6a76b275efbaa126b31119dc197b927a5 Link: https://lore.kernel.org/r/d6609065f8f40397a4124654eb68c9f490b4d477.1616123271.git.pcc@google.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
parent
2f79d2fc39
commit
201698626f
|
@ -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
|
KVM_GET/SET_REG_* ioctls and mask those features from cpufeature ID
|
||||||
register. Any attempt to use the Pointer Authentication instructions will
|
register. Any attempt to use the Pointer Authentication instructions will
|
||||||
result in an UNDEFINED exception being injected into the guest.
|
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.
|
||||||
|
|
|
@ -39,7 +39,7 @@ void mte_free_tag_storage(char *storage);
|
||||||
|
|
||||||
void mte_sync_tags(pte_t *ptep, pte_t pte);
|
void mte_sync_tags(pte_t *ptep, pte_t pte);
|
||||||
void mte_copy_page_tags(void *kto, const void *kfrom);
|
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_thread_switch(struct task_struct *next);
|
||||||
void mte_suspend_enter(void);
|
void mte_suspend_enter(void);
|
||||||
void mte_suspend_exit(void);
|
void mte_suspend_exit(void);
|
||||||
|
@ -61,7 +61,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 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)
|
static inline void mte_thread_switch(struct task_struct *next)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#define __ASM_POINTER_AUTH_H
|
#define __ASM_POINTER_AUTH_H
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/prctl.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
|
@ -71,6 +72,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_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)
|
static inline unsigned long ptrauth_strip_insn_pac(unsigned long ptr)
|
||||||
{
|
{
|
||||||
return ptrauth_clear_pac(ptr);
|
return ptrauth_clear_pac(ptr);
|
||||||
|
@ -85,8 +90,17 @@ static __always_inline void ptrauth_enable(void)
|
||||||
isb();
|
isb();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ptrauth_thread_init_user(tsk) \
|
#define ptrauth_thread_init_user() \
|
||||||
ptrauth_keys_init_user(&(tsk)->thread.keys_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) \
|
#define ptrauth_thread_init_kernel(tsk) \
|
||||||
ptrauth_keys_init_kernel(&(tsk)->thread.keys_kernel)
|
ptrauth_keys_init_kernel(&(tsk)->thread.keys_kernel)
|
||||||
#define ptrauth_thread_switch_kernel(tsk) \
|
#define ptrauth_thread_switch_kernel(tsk) \
|
||||||
|
@ -95,10 +109,15 @@ static __always_inline void ptrauth_enable(void)
|
||||||
#else /* CONFIG_ARM64_PTR_AUTH */
|
#else /* CONFIG_ARM64_PTR_AUTH */
|
||||||
#define ptrauth_enable()
|
#define ptrauth_enable()
|
||||||
#define ptrauth_prctl_reset_keys(tsk, arg) (-EINVAL)
|
#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_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_init_kernel(tsk)
|
||||||
#define ptrauth_thread_switch_kernel(tsk)
|
#define ptrauth_thread_switch_kernel(tsk)
|
||||||
#endif /* CONFIG_ARM64_PTR_AUTH */
|
#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 */
|
#endif /* __ASM_POINTER_AUTH_H */
|
||||||
|
|
|
@ -156,7 +156,9 @@ struct thread_struct {
|
||||||
u64 sctlr_user;
|
u64 sctlr_user;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SCTLR_USER_MASK SCTLR_EL1_TCF0_MASK
|
#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,
|
static inline void arch_thread_struct_whitelist(unsigned long *offset,
|
||||||
unsigned long *size)
|
unsigned long *size)
|
||||||
|
@ -305,6 +307,11 @@ extern void __init minsigstksz_setup(void);
|
||||||
/* PR_PAC_RESET_KEYS prctl */
|
/* PR_PAC_RESET_KEYS prctl */
|
||||||
#define PAC_RESET_KEYS(tsk, arg) ptrauth_prctl_reset_keys(tsk, arg)
|
#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
|
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
|
||||||
/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
|
/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
|
||||||
long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg);
|
long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg);
|
||||||
|
|
|
@ -565,8 +565,10 @@
|
||||||
#define SCTLR_ELx_TCF_ASYNC (UL(0x2) << SCTLR_ELx_TCF_SHIFT)
|
#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_TCF_MASK (UL(0x3) << SCTLR_ELx_TCF_SHIFT)
|
||||||
|
|
||||||
|
#define SCTLR_ELx_ENIA_SHIFT 31
|
||||||
|
|
||||||
#define SCTLR_ELx_ITFSB (BIT(37))
|
#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_ENIB (BIT(30))
|
||||||
#define SCTLR_ELx_ENDA (BIT(27))
|
#define SCTLR_ELx_ENDA (BIT(27))
|
||||||
#define SCTLR_ELx_EE (BIT(25))
|
#define SCTLR_ELx_EE (BIT(25))
|
||||||
|
|
|
@ -43,6 +43,7 @@ int main(void)
|
||||||
#endif
|
#endif
|
||||||
BLANK();
|
BLANK();
|
||||||
DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context));
|
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
|
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||||
DEFINE(THREAD_KEYS_USER, offsetof(struct task_struct, thread.keys_user));
|
DEFINE(THREAD_KEYS_USER, offsetof(struct task_struct, thread.keys_user));
|
||||||
DEFINE(THREAD_KEYS_KERNEL, offsetof(struct task_struct, thread.keys_kernel));
|
DEFINE(THREAD_KEYS_KERNEL, offsetof(struct task_struct, thread.keys_kernel));
|
||||||
|
|
|
@ -247,7 +247,24 @@ alternative_else_nop_endif
|
||||||
check_mte_async_tcf x19, x22
|
check_mte_async_tcf x19, x22
|
||||||
apply_ssbd 1, x22, x23
|
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. 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.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
|
||||||
mte_set_kernel_gcr x22, x23
|
mte_set_kernel_gcr x22, x23
|
||||||
|
|
||||||
|
@ -351,9 +368,27 @@ alternative_else_nop_endif
|
||||||
3:
|
3:
|
||||||
scs_save tsk, x0
|
scs_save tsk, x0
|
||||||
|
|
||||||
/* No kernel C function calls after this as user keys are set. */
|
/*
|
||||||
|
* No kernel C function calls after this as user keys are set and IA may
|
||||||
|
* be disabled.
|
||||||
|
*/
|
||||||
ptrauth_keys_install_user tsk, x0, x1, x2
|
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
|
||||||
|
|
||||||
mte_set_user_gcr tsk, x0, x1
|
mte_set_user_gcr tsk, x0, x1
|
||||||
|
|
||||||
apply_ssbd 0, x0, x1
|
apply_ssbd 0, x0, x1
|
||||||
|
|
|
@ -207,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())
|
if (!system_supports_mte())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -46,3 +46,65 @@ int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg)
|
||||||
|
|
||||||
return 0;
|
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();
|
tls_thread_flush();
|
||||||
flush_ptrace_hw_breakpoint(current);
|
flush_ptrace_hw_breakpoint(current);
|
||||||
flush_tagged_addr_state();
|
flush_tagged_addr_state();
|
||||||
flush_mte_state();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void release_thread(struct task_struct *dead_task)
|
void release_thread(struct task_struct *dead_task)
|
||||||
|
@ -531,7 +530,11 @@ static void erratum_1418040_thread_switch(struct task_struct *prev,
|
||||||
|
|
||||||
static void update_sctlr_el1(u64 sctlr)
|
static void update_sctlr_el1(u64 sctlr)
|
||||||
{
|
{
|
||||||
sysreg_clear_set(sctlr_el1, SCTLR_USER_MASK, 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 required for the kernel uaccess routines when setting TCF0. */
|
||||||
isb();
|
isb();
|
||||||
|
@ -632,7 +635,8 @@ void arch_setup_new_exec(void)
|
||||||
{
|
{
|
||||||
current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
|
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)) {
|
if (task_spec_ssb_noexec(current)) {
|
||||||
arch_prctl_spec_ctrl_set(current, PR_SPEC_STORE_BYPASS,
|
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));
|
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
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||||
static __uint128_t pac_key_to_user(const struct ptrauth_key *key)
|
static __uint128_t pac_key_to_user(const struct ptrauth_key *key)
|
||||||
{
|
{
|
||||||
|
@ -1074,6 +1106,7 @@ enum aarch64_regset {
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_ARM64_PTR_AUTH
|
#ifdef CONFIG_ARM64_PTR_AUTH
|
||||||
REGSET_PAC_MASK,
|
REGSET_PAC_MASK,
|
||||||
|
REGSET_PAC_ENABLED_KEYS,
|
||||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||||
REGSET_PACA_KEYS,
|
REGSET_PACA_KEYS,
|
||||||
REGSET_PACG_KEYS,
|
REGSET_PACG_KEYS,
|
||||||
|
@ -1160,6 +1193,14 @@ static const struct user_regset aarch64_regsets[] = {
|
||||||
.regset_get = pac_mask_get,
|
.regset_get = pac_mask_get,
|
||||||
/* this cannot be set dynamically */
|
/* 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
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||||
[REGSET_PACA_KEYS] = {
|
[REGSET_PACA_KEYS] = {
|
||||||
.core_note_type = NT_ARM_PACA_KEYS,
|
.core_note_type = NT_ARM_PACA_KEYS,
|
||||||
|
|
|
@ -426,6 +426,7 @@ typedef struct elf64_shdr {
|
||||||
#define NT_ARM_PACA_KEYS 0x407 /* ARM pointer authentication address keys */
|
#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_PACG_KEYS 0x408 /* ARM pointer authentication generic key */
|
||||||
#define NT_ARM_TAGGED_ADDR_CTRL 0x409 /* arm64 tagged address control (prctl()) */
|
#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_ARC_V2 0x600 /* ARCv2 accumulator/extra registers */
|
||||||
#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note */
|
#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note */
|
||||||
#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */
|
#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_ALLOW 0
|
||||||
# define SYSCALL_DISPATCH_FILTER_BLOCK 1
|
# 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 */
|
#endif /* _LINUX_PRCTL_H */
|
||||||
|
|
16
kernel/sys.c
16
kernel/sys.c
|
@ -119,6 +119,12 @@
|
||||||
#ifndef PAC_RESET_KEYS
|
#ifndef PAC_RESET_KEYS
|
||||||
# define PAC_RESET_KEYS(a, b) (-EINVAL)
|
# define PAC_RESET_KEYS(a, b) (-EINVAL)
|
||||||
#endif
|
#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
|
#ifndef SET_TAGGED_ADDR_CTRL
|
||||||
# define SET_TAGGED_ADDR_CTRL(a) (-EINVAL)
|
# define SET_TAGGED_ADDR_CTRL(a) (-EINVAL)
|
||||||
#endif
|
#endif
|
||||||
|
@ -2497,6 +2503,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
error = PAC_RESET_KEYS(me, arg2);
|
error = PAC_RESET_KEYS(me, arg2);
|
||||||
break;
|
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:
|
case PR_SET_TAGGED_ADDR_CTRL:
|
||||||
if (arg3 || arg4 || arg5)
|
if (arg3 || arg4 || arg5)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
Loading…
Reference in New Issue