[PATCH] arm64: add prctl(PR_PAC_SET_ENABLED_KEYS)

Peter Collingbourne pcc at google.com
Fri Jul 31 21:13:14 EDT 2020


On Fri, Jul 31, 2020 at 6:11 PM Peter Collingbourne <pcc at google.com> 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 at google.com>

Sorry, will fix in v2.

Peter

> ---
>  .../arm64/pointer-authentication.rst          | 27 +++++++++++++++
>  arch/arm64/include/asm/asm_pointer_auth.h     | 19 +++++++++++
>  arch/arm64/include/asm/pointer_auth.h         | 10 ++++--
>  arch/arm64/include/asm/processor.h            |  5 +++
>  arch/arm64/kernel/asm-offsets.c               |  1 +
>  arch/arm64/kernel/pointer_auth.c              | 34 +++++++++++++++++++
>  include/uapi/linux/prctl.h                    |  3 ++
>  kernel/sys.c                                  |  8 +++++
>  8 files changed, 105 insertions(+), 2 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/asm_pointer_auth.h b/arch/arm64/include/asm/asm_pointer_auth.h
> index 52dead2a8640..d121fa5fed5f 100644
> --- a/arch/arm64/include/asm/asm_pointer_auth.h
> +++ b/arch/arm64/include/asm/asm_pointer_auth.h
> @@ -31,6 +31,14 @@ alternative_else_nop_endif
>         ldp     \tmp2, \tmp3, [\tmp1, #PTRAUTH_USER_KEY_APDB]
>         msr_s   SYS_APDBKEYLO_EL1, \tmp2
>         msr_s   SYS_APDBKEYHI_EL1, \tmp3
> +
> +       ldr     \tmp2, [\tsk, #THREAD_SCTLR_ENXX_MASK]
> +       cbz     \tmp2, .Laddr_auth_skip_\@
> +
> +       mrs_s   \tmp3, SYS_SCTLR_EL1
> +       bic     \tmp3, \tmp3, \tmp2
> +       msr_s   SYS_SCTLR_EL1, \tmp3
> +
>  .Laddr_auth_skip_\@:
>  alternative_if ARM64_HAS_GENERIC_AUTH
>         ldp     \tmp2, \tmp3, [\tmp1, #PTRAUTH_USER_KEY_APGA]
> @@ -45,6 +53,17 @@ alternative_else_nop_endif
>         ldp     \tmp2, \tmp3, [\tmp1, #PTRAUTH_KERNEL_KEY_APIA]
>         msr_s   SYS_APIAKEYLO_EL1, \tmp2
>         msr_s   SYS_APIAKEYHI_EL1, \tmp3
> +
> +       ldr     \tmp2, [\tsk, #THREAD_SCTLR_ENXX_MASK]
> +       cbz     \tmp2, .Lset_sctlr_skip_\@
> +
> +       mrs_s   \tmp1, SYS_SCTLR_EL1
> +       mov     \tmp2, #(SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | SCTLR_ELx_ENDA)
> +       movk    \tmp2, #SCTLR_ELx_ENDB
> +       orr     \tmp1, \tmp1, \tmp2
> +       msr_s   SYS_SCTLR_EL1, \tmp1
> +
> +.Lset_sctlr_skip_\@:
>         .endm
>
>         .macro ptrauth_keys_install_kernel_nosync tsk, tmp1, tmp2, tmp3
> diff --git a/arch/arm64/include/asm/pointer_auth.h b/arch/arm64/include/asm/pointer_auth.h
> index c6b4f0603024..d4c375454a36 100644
> --- a/arch/arm64/include/asm/pointer_auth.h
> +++ b/arch/arm64/include/asm/pointer_auth.h
> @@ -70,14 +70,19 @@ 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);
>
>  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(tsk) do {                             \
> +               ptrauth_keys_init_user(&(tsk)->thread.keys_user);       \
> +               (tsk)->thread.sctlr_enxx_mask = 0;                      \
> +       } while (0)
>  #define ptrauth_thread_init_kernel(tsk)                                        \
>         ptrauth_keys_init_kernel(&(tsk)->thread.keys_kernel)
>  #define ptrauth_thread_switch_kernel(tsk)                              \
> @@ -85,6 +90,7 @@ 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_strip_insn_pac(lr)     (lr)
>  #define ptrauth_thread_init_user(tsk)
>  #define ptrauth_thread_init_kernel(tsk)
> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
> index 240fe5e5b720..6974d227b01f 100644
> --- a/arch/arm64/include/asm/processor.h
> +++ b/arch/arm64/include/asm/processor.h
> @@ -150,6 +150,7 @@ struct thread_struct {
>  #ifdef CONFIG_ARM64_PTR_AUTH
>         struct ptrauth_keys_user        keys_user;
>         struct ptrauth_keys_kernel      keys_kernel;
> +       u64                             sctlr_enxx_mask;
>  #endif
>  };
>
> @@ -313,6 +314,10 @@ 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_ENABLED_KEYS prctl */
> +#define PAC_SET_ENABLED_KEYS(tsk, keys, enabled)                               \
> +       ptrauth_prctl_set_enabled_keys(tsk, keys, enabled)
> +
>  #ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
>  /* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
>  long set_tagged_addr_ctrl(unsigned long arg);
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index 0577e2142284..dac80e16fe35 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -47,6 +47,7 @@ int main(void)
>  #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));
> +  DEFINE(THREAD_SCTLR_ENXX_MASK,offsetof(struct task_struct, thread.sctlr_enxx_mask));
>  #endif
>    BLANK();
>    DEFINE(S_X0,                 offsetof(struct pt_regs, regs[0]));
> diff --git a/arch/arm64/kernel/pointer_auth.c b/arch/arm64/kernel/pointer_auth.c
> index 1e77736a4f66..8c385b7f324a 100644
> --- a/arch/arm64/kernel/pointer_auth.c
> +++ b/arch/arm64/kernel/pointer_auth.c
> @@ -42,3 +42,37 @@ 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_enxx_mask = tsk->thread.sctlr_enxx_mask;
> +       unsigned long addr_key_mask = PR_PAC_APIAKEY | PR_PAC_APIBKEY |
> +                                     PR_PAC_APDAKEY | PR_PAC_APDBKEY;
> +
> +       if (!system_supports_address_auth())
> +               return -EINVAL;
> +
> +       if ((keys & ~addr_key_mask) || (enabled & ~keys))
> +               return -EINVAL;
> +
> +       sctlr_enxx_mask |= arg_to_enxx_mask(keys);
> +       sctlr_enxx_mask &= ~arg_to_enxx_mask(enabled);
> +
> +       tsk->thread.sctlr_enxx_mask = sctlr_enxx_mask;
> +       return 0;
> +}
> diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
> index 07b4f8131e36..18e1ae4a37a2 100644
> --- a/include/uapi/linux/prctl.h
> +++ b/include/uapi/linux/prctl.h
> @@ -238,4 +238,7 @@ struct prctl_mm_map {
>  #define PR_SET_IO_FLUSHER              57
>  #define PR_GET_IO_FLUSHER              58
>
> +/* Set enabled arm64 pointer authentication keys */
> +#define PR_PAC_SET_ENABLED_KEYS                59
> +
>  #endif /* _LINUX_PRCTL_H */
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 00a96746e28a..623df216183b 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -119,6 +119,9 @@
>  #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 SET_TAGGED_ADDR_CTRL
>  # define SET_TAGGED_ADDR_CTRL(a)       (-EINVAL)
>  #endif
> @@ -2494,6 +2497,11 @@ 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_SET_TAGGED_ADDR_CTRL:
>                 if (arg3 || arg4 || arg5)
>                         return -EINVAL;
> --
> 2.28.0.163.g6104cc2f0b6-goog
>



More information about the linux-arm-kernel mailing list