Commit 20169862 authored by Peter Collingbourne's avatar Peter Collingbourne Committed by Catalin Marinas
Browse files

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: default avatarPeter Collingbourne <pcc@google.com>
Reviewed-by: default avatarDave 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: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 2f79d2fc
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -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.
+2 −2
Original line number Diff line number Diff line
@@ -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);
@@ -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 flush_mte_state(void)
static inline void mte_thread_init_user(void)
{
}
static inline void mte_thread_switch(struct task_struct *next)
+22 −3
Original line number Diff line number Diff line
@@ -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,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_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 +90,17 @@ static __always_inline void ptrauth_enable(void)
	isb();
}

#define ptrauth_thread_init_user(tsk)					\
	ptrauth_keys_init_user(&(tsk)->thread.keys_user)
#define ptrauth_thread_init_user()                                             \
	do {                                                                   \
		ptrauth_keys_init_user(&current->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)				\
@@ -95,10 +109,15 @@ 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_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 */
+8 −1
Original line number Diff line number Diff line
@@ -156,7 +156,9 @@ struct thread_struct {
	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,
						unsigned long *size)
@@ -305,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);
+3 −1
Original line number Diff line number Diff line
@@ -565,8 +565,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))
Loading