mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-05 18:14:07 +08:00
b90e483938
The kernel does not use any keys besides IA so we don't need to install IB/DA/DB/GA on kernel exit if we arrange to install them on task switch instead, which we can expect to happen an order of magnitude less often. Furthermore we can avoid installing the user IA in the case where the user task has IA disabled and just leave the kernel IA installed. This also lets us avoid needing to install IA on kernel entry. On an Apple M1 under a hypervisor, the overhead of kernel entry/exit has been measured to be reduced by 15.6ns in the case where IA is enabled, and 31.9ns in the case where IA is disabled. Signed-off-by: Peter Collingbourne <pcc@google.com> Link: https://linux-review.googlesource.com/id/Ieddf6b580d23c9e0bed45a822dabe72d2ffc9a8e Link: https://lore.kernel.org/r/2d653d055f38f779937f2b92f8ddd5cf9e4af4f4.1616123271.git.pcc@google.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
112 lines
2.8 KiB
C
112 lines
2.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <linux/compat.h>
|
|
#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_user *keys = &tsk->thread.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 (is_compat_thread(task_thread_info(tsk)))
|
|
return -EINVAL;
|
|
|
|
if (!arg) {
|
|
ptrauth_keys_init_user(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_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;
|
|
}
|