Patchwork [v2] arm64: add ptrace regsets for ptrauth key management

login
register
mail settings
Submitter Kristina Martsenko
Date Jan. 10, 2019, 7:35 p.m.
Message ID <20190110193508.31888-1-kristina.martsenko@arm.com>
Download mbox | patch
Permalink /patch/697233/
State New
Headers show

Comments

Kristina Martsenko - Jan. 10, 2019, 7:35 p.m.
Add two new ptrace regsets, which can be used to request and change the
pointer authentication keys of a thread. NT_ARM_PACA_KEYS gives access
to the instruction/data address keys, and NT_ARM_PACG_KEYS to the
generic authentication key. The keys are also part of the core dump file
of the process.

The regsets are only exposed if the kernel is compiled with
CONFIG_CHECKPOINT_RESTORE=y, as the intended use case is checkpointing
and restoring processes that are using pointer authentication. Normally
applications or debuggers should not need to know the keys (and exposing
the keys is a security risk), so the regsets are not exposed by default.

Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
---

The previous version of this patch was:
  https://lore.kernel.org/lkml/20181207183931.4285-12-kristina.martsenko@arm.com/

Changes in v2:
 - Convert each field individually between ptrauth_keys and
   user_pac_address_keys/user_pac_generic_keys

 Documentation/arm64/pointer-authentication.txt |   5 +
 arch/arm64/include/uapi/asm/ptrace.h           |  18 ++++
 arch/arm64/kernel/ptrace.c                     | 144 +++++++++++++++++++++++++
 include/uapi/linux/elf.h                       |   2 +
 4 files changed, 169 insertions(+)
Dave Martin - Jan. 11, 2019, 1:31 p.m.
On Thu, Jan 10, 2019 at 07:35:08PM +0000, Kristina Martsenko wrote:
> Add two new ptrace regsets, which can be used to request and change the
> pointer authentication keys of a thread. NT_ARM_PACA_KEYS gives access
> to the instruction/data address keys, and NT_ARM_PACG_KEYS to the
> generic authentication key. The keys are also part of the core dump file
> of the process.
> 
> The regsets are only exposed if the kernel is compiled with
> CONFIG_CHECKPOINT_RESTORE=y, as the intended use case is checkpointing
> and restoring processes that are using pointer authentication. Normally
> applications or debuggers should not need to know the keys (and exposing
> the keys is a security risk), so the regsets are not exposed by default.

I think the second variant of the patch is the way to go with this, since
it seems to result in simpler code.

So, I'll comment on the second patch.

Cheers
---Dave

> 
> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
> ---
> 
> The previous version of this patch was:
>   https://lore.kernel.org/lkml/20181207183931.4285-12-kristina.martsenko@arm.com/
> 
> Changes in v2:
>  - Convert each field individually between ptrauth_keys and
>    user_pac_address_keys/user_pac_generic_keys
> 
>  Documentation/arm64/pointer-authentication.txt |   5 +
>  arch/arm64/include/uapi/asm/ptrace.h           |  18 ++++
>  arch/arm64/kernel/ptrace.c                     | 144 +++++++++++++++++++++++++
>  include/uapi/linux/elf.h                       |   2 +
>  4 files changed, 169 insertions(+)
> 
> diff --git a/Documentation/arm64/pointer-authentication.txt b/Documentation/arm64/pointer-authentication.txt
> index a25cd21290e9..5baca42ba146 100644
> --- a/Documentation/arm64/pointer-authentication.txt
> +++ b/Documentation/arm64/pointer-authentication.txt
> @@ -78,6 +78,11 @@ bits can vary between the two. Note that the masks apply to TTBR0
>  addresses, and are not valid to apply to TTBR1 addresses (e.g. kernel
>  pointers).
>  
> +Additionally, when CONFIG_CHECKPOINT_RESTORE is also set, the kernel
> +will expose the NT_ARM_PACA_KEYS and NT_ARM_PACG_KEYS regsets (struct
> +user_pac_address_keys and struct user_pac_generic_keys). These can be
> +used to get and set the keys for a thread.
> +
>  
>  Virtualization
>  --------------
> diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h
> index 28d77c9ed531..0affa43602a5 100644
> --- a/arch/arm64/include/uapi/asm/ptrace.h
> +++ b/arch/arm64/include/uapi/asm/ptrace.h
> @@ -233,6 +233,24 @@ struct user_pac_mask {
>  	__u64		insn_mask;
>  };
>  
> +/* pointer authentication keys (NT_ARM_PACA_KEYS, NT_ARM_PACG_KEYS) */
> +
> +struct user_pac_address_keys {
> +	__u64		apiakey_lo;
> +	__u64		apiakey_hi;
> +	__u64		apibkey_lo;
> +	__u64		apibkey_hi;
> +	__u64		apdakey_lo;
> +	__u64		apdakey_hi;
> +	__u64		apdbkey_lo;
> +	__u64		apdbkey_hi;
> +};
> +
> +struct user_pac_generic_keys {
> +	__u64		apgakey_lo;
> +	__u64		apgakey_hi;
> +};
> +
>  #endif /* __ASSEMBLY__ */
>  
>  #endif /* _UAPI__ASM_PTRACE_H */
> diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> index 9dce33b0e260..a5f9f07e9eac 100644
> --- a/arch/arm64/kernel/ptrace.c
> +++ b/arch/arm64/kernel/ptrace.c
> @@ -979,6 +979,128 @@ static int pac_mask_get(struct task_struct *target,
>  
>  	return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &uregs, 0, -1);
>  }
> +
> +#ifdef CONFIG_CHECKPOINT_RESTORE
> +static void pac_address_keys_to_user(struct user_pac_address_keys *ukeys,
> +				     struct ptrauth_keys *keys)
> +{
> +	ukeys->apiakey_lo = keys->apia.lo;
> +	ukeys->apiakey_hi = keys->apia.hi;
> +	ukeys->apibkey_lo = keys->apib.lo;
> +	ukeys->apibkey_hi = keys->apib.hi;
> +	ukeys->apdakey_lo = keys->apda.lo;
> +	ukeys->apdakey_hi = keys->apda.hi;
> +	ukeys->apdbkey_lo = keys->apdb.lo;
> +	ukeys->apdbkey_hi = keys->apdb.hi;
> +}
> +
> +static void pac_address_keys_from_user(struct user_pac_address_keys *ukeys,
> +				       struct ptrauth_keys *keys)
> +{
> +	keys->apia.lo = ukeys->apiakey_lo;
> +	keys->apia.hi = ukeys->apiakey_hi;
> +	keys->apib.lo = ukeys->apibkey_lo;
> +	keys->apib.hi = ukeys->apibkey_hi;
> +	keys->apda.lo = ukeys->apdakey_lo;
> +	keys->apda.hi = ukeys->apdakey_hi;
> +	keys->apdb.lo = ukeys->apdbkey_lo;
> +	keys->apdb.hi = ukeys->apdbkey_hi;
> +}
> +
> +static int pac_address_keys_get(struct task_struct *target,
> +				const struct user_regset *regset,
> +				unsigned int pos, unsigned int count,
> +				void *kbuf, void __user *ubuf)
> +{
> +	struct ptrauth_keys *keys = &target->thread.keys_user;
> +	struct user_pac_address_keys user_keys;
> +
> +	if (!system_supports_address_auth())
> +		return -EINVAL;
> +
> +	memset(&user_keys, 0, sizeof(user_keys));
> +	pac_address_keys_to_user(&user_keys, keys);
> +
> +	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> +				   &user_keys, 0, -1);
> +}
> +
> +static int pac_address_keys_set(struct task_struct *target,
> +				const struct user_regset *regset,
> +				unsigned int pos, unsigned int count,
> +				const void *kbuf, const void __user *ubuf)
> +{
> +	struct ptrauth_keys *keys = &target->thread.keys_user;
> +	struct user_pac_address_keys user_keys;
> +	int ret;
> +
> +	if (!system_supports_address_auth())
> +		return -EINVAL;
> +
> +	pac_address_keys_to_user(&user_keys, keys);
> +	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> +				 &user_keys, 0, -1);
> +	if (ret)
> +		return ret;
> +	pac_address_keys_from_user(&user_keys, keys);
> +
> +	return 0;
> +}
> +
> +static void pac_generic_keys_to_user(struct user_pac_generic_keys *ukeys,
> +				     struct ptrauth_keys *keys)
> +{
> +	ukeys->apgakey_lo = keys->apga.lo;
> +	ukeys->apgakey_hi = keys->apga.hi;
> +}
> +
> +static void pac_generic_keys_from_user(struct user_pac_generic_keys *ukeys,
> +				       struct ptrauth_keys *keys)
> +{
> +	keys->apga.lo = ukeys->apgakey_lo;
> +	keys->apga.hi = ukeys->apgakey_hi;
> +}
> +
> +static int pac_generic_keys_get(struct task_struct *target,
> +				const struct user_regset *regset,
> +				unsigned int pos, unsigned int count,
> +				void *kbuf, void __user *ubuf)
> +{
> +	struct ptrauth_keys *keys = &target->thread.keys_user;
> +	struct user_pac_generic_keys user_keys;
> +
> +	if (!system_supports_generic_auth())
> +		return -EINVAL;
> +
> +	memset(&user_keys, 0, sizeof(user_keys));
> +	pac_generic_keys_to_user(&user_keys, keys);
> +
> +	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> +				   &user_keys, 0, -1);
> +}
> +
> +static int pac_generic_keys_set(struct task_struct *target,
> +				const struct user_regset *regset,
> +				unsigned int pos, unsigned int count,
> +				const void *kbuf, const void __user *ubuf)
> +{
> +	struct ptrauth_keys *keys = &target->thread.keys_user;
> +	struct user_pac_generic_keys user_keys;
> +	int ret;
> +
> +	if (!system_supports_generic_auth())
> +		return -EINVAL;
> +
> +	pac_generic_keys_to_user(&user_keys, keys);
> +	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> +				 &user_keys, 0, -1);
> +	if (ret)
> +		return ret;
> +	pac_generic_keys_from_user(&user_keys, keys);
> +
> +	return 0;
> +}
> +#endif /* CONFIG_CHECKPOINT_RESTORE */
>  #endif /* CONFIG_ARM64_PTR_AUTH */
>  
>  enum aarch64_regset {
> @@ -995,6 +1117,10 @@ enum aarch64_regset {
>  #endif
>  #ifdef CONFIG_ARM64_PTR_AUTH
>  	REGSET_PAC_MASK,
> +#ifdef CONFIG_CHECKPOINT_RESTORE
> +	REGSET_PACA_KEYS,
> +	REGSET_PACG_KEYS,
> +#endif
>  #endif
>  };
>  
> @@ -1074,6 +1200,24 @@ static const struct user_regset aarch64_regsets[] = {
>  		.get = pac_mask_get,
>  		/* this cannot be set dynamically */
>  	},
> +#ifdef CONFIG_CHECKPOINT_RESTORE
> +	[REGSET_PACA_KEYS] = {
> +		.core_note_type = NT_ARM_PACA_KEYS,
> +		.n = sizeof(struct user_pac_address_keys) / sizeof(u64),
> +		.size = sizeof(u64),
> +		.align = sizeof(u64),
> +		.get = pac_address_keys_get,
> +		.set = pac_address_keys_set,
> +	},
> +	[REGSET_PACG_KEYS] = {
> +		.core_note_type = NT_ARM_PACG_KEYS,
> +		.n = sizeof(struct user_pac_generic_keys) / sizeof(u64),
> +		.size = sizeof(u64),
> +		.align = sizeof(u64),
> +		.get = pac_generic_keys_get,
> +		.set = pac_generic_keys_set,
> +	},
> +#endif
>  #endif
>  };
>  
> diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
> index e4d6ddd93567..34c02e4290fe 100644
> --- a/include/uapi/linux/elf.h
> +++ b/include/uapi/linux/elf.h
> @@ -421,6 +421,8 @@ typedef struct elf64_shdr {
>  #define NT_ARM_SYSTEM_CALL	0x404	/* ARM system call number */
>  #define NT_ARM_SVE	0x405		/* ARM Scalable Vector Extension registers */
>  #define NT_ARM_PAC_MASK		0x406	/* ARM pointer authentication code masks */
> +#define NT_ARM_PACA_KEYS	0x407	/* ARM pointer authentication address keys */
> +#define NT_ARM_PACG_KEYS	0x408	/* ARM pointer authentication generic key */
>  #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 */
> -- 
> 2.11.0
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

Patch

diff --git a/Documentation/arm64/pointer-authentication.txt b/Documentation/arm64/pointer-authentication.txt
index a25cd21290e9..5baca42ba146 100644
--- a/Documentation/arm64/pointer-authentication.txt
+++ b/Documentation/arm64/pointer-authentication.txt
@@ -78,6 +78,11 @@  bits can vary between the two. Note that the masks apply to TTBR0
 addresses, and are not valid to apply to TTBR1 addresses (e.g. kernel
 pointers).
 
+Additionally, when CONFIG_CHECKPOINT_RESTORE is also set, the kernel
+will expose the NT_ARM_PACA_KEYS and NT_ARM_PACG_KEYS regsets (struct
+user_pac_address_keys and struct user_pac_generic_keys). These can be
+used to get and set the keys for a thread.
+
 
 Virtualization
 --------------
diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h
index 28d77c9ed531..0affa43602a5 100644
--- a/arch/arm64/include/uapi/asm/ptrace.h
+++ b/arch/arm64/include/uapi/asm/ptrace.h
@@ -233,6 +233,24 @@  struct user_pac_mask {
 	__u64		insn_mask;
 };
 
+/* pointer authentication keys (NT_ARM_PACA_KEYS, NT_ARM_PACG_KEYS) */
+
+struct user_pac_address_keys {
+	__u64		apiakey_lo;
+	__u64		apiakey_hi;
+	__u64		apibkey_lo;
+	__u64		apibkey_hi;
+	__u64		apdakey_lo;
+	__u64		apdakey_hi;
+	__u64		apdbkey_lo;
+	__u64		apdbkey_hi;
+};
+
+struct user_pac_generic_keys {
+	__u64		apgakey_lo;
+	__u64		apgakey_hi;
+};
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* _UAPI__ASM_PTRACE_H */
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 9dce33b0e260..a5f9f07e9eac 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -979,6 +979,128 @@  static int pac_mask_get(struct task_struct *target,
 
 	return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &uregs, 0, -1);
 }
+
+#ifdef CONFIG_CHECKPOINT_RESTORE
+static void pac_address_keys_to_user(struct user_pac_address_keys *ukeys,
+				     struct ptrauth_keys *keys)
+{
+	ukeys->apiakey_lo = keys->apia.lo;
+	ukeys->apiakey_hi = keys->apia.hi;
+	ukeys->apibkey_lo = keys->apib.lo;
+	ukeys->apibkey_hi = keys->apib.hi;
+	ukeys->apdakey_lo = keys->apda.lo;
+	ukeys->apdakey_hi = keys->apda.hi;
+	ukeys->apdbkey_lo = keys->apdb.lo;
+	ukeys->apdbkey_hi = keys->apdb.hi;
+}
+
+static void pac_address_keys_from_user(struct user_pac_address_keys *ukeys,
+				       struct ptrauth_keys *keys)
+{
+	keys->apia.lo = ukeys->apiakey_lo;
+	keys->apia.hi = ukeys->apiakey_hi;
+	keys->apib.lo = ukeys->apibkey_lo;
+	keys->apib.hi = ukeys->apibkey_hi;
+	keys->apda.lo = ukeys->apdakey_lo;
+	keys->apda.hi = ukeys->apdakey_hi;
+	keys->apdb.lo = ukeys->apdbkey_lo;
+	keys->apdb.hi = ukeys->apdbkey_hi;
+}
+
+static int pac_address_keys_get(struct task_struct *target,
+				const struct user_regset *regset,
+				unsigned int pos, unsigned int count,
+				void *kbuf, void __user *ubuf)
+{
+	struct ptrauth_keys *keys = &target->thread.keys_user;
+	struct user_pac_address_keys user_keys;
+
+	if (!system_supports_address_auth())
+		return -EINVAL;
+
+	memset(&user_keys, 0, sizeof(user_keys));
+	pac_address_keys_to_user(&user_keys, keys);
+
+	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+				   &user_keys, 0, -1);
+}
+
+static int pac_address_keys_set(struct task_struct *target,
+				const struct user_regset *regset,
+				unsigned int pos, unsigned int count,
+				const void *kbuf, const void __user *ubuf)
+{
+	struct ptrauth_keys *keys = &target->thread.keys_user;
+	struct user_pac_address_keys user_keys;
+	int ret;
+
+	if (!system_supports_address_auth())
+		return -EINVAL;
+
+	pac_address_keys_to_user(&user_keys, keys);
+	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				 &user_keys, 0, -1);
+	if (ret)
+		return ret;
+	pac_address_keys_from_user(&user_keys, keys);
+
+	return 0;
+}
+
+static void pac_generic_keys_to_user(struct user_pac_generic_keys *ukeys,
+				     struct ptrauth_keys *keys)
+{
+	ukeys->apgakey_lo = keys->apga.lo;
+	ukeys->apgakey_hi = keys->apga.hi;
+}
+
+static void pac_generic_keys_from_user(struct user_pac_generic_keys *ukeys,
+				       struct ptrauth_keys *keys)
+{
+	keys->apga.lo = ukeys->apgakey_lo;
+	keys->apga.hi = ukeys->apgakey_hi;
+}
+
+static int pac_generic_keys_get(struct task_struct *target,
+				const struct user_regset *regset,
+				unsigned int pos, unsigned int count,
+				void *kbuf, void __user *ubuf)
+{
+	struct ptrauth_keys *keys = &target->thread.keys_user;
+	struct user_pac_generic_keys user_keys;
+
+	if (!system_supports_generic_auth())
+		return -EINVAL;
+
+	memset(&user_keys, 0, sizeof(user_keys));
+	pac_generic_keys_to_user(&user_keys, keys);
+
+	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+				   &user_keys, 0, -1);
+}
+
+static int pac_generic_keys_set(struct task_struct *target,
+				const struct user_regset *regset,
+				unsigned int pos, unsigned int count,
+				const void *kbuf, const void __user *ubuf)
+{
+	struct ptrauth_keys *keys = &target->thread.keys_user;
+	struct user_pac_generic_keys user_keys;
+	int ret;
+
+	if (!system_supports_generic_auth())
+		return -EINVAL;
+
+	pac_generic_keys_to_user(&user_keys, keys);
+	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				 &user_keys, 0, -1);
+	if (ret)
+		return ret;
+	pac_generic_keys_from_user(&user_keys, keys);
+
+	return 0;
+}
+#endif /* CONFIG_CHECKPOINT_RESTORE */
 #endif /* CONFIG_ARM64_PTR_AUTH */
 
 enum aarch64_regset {
@@ -995,6 +1117,10 @@  enum aarch64_regset {
 #endif
 #ifdef CONFIG_ARM64_PTR_AUTH
 	REGSET_PAC_MASK,
+#ifdef CONFIG_CHECKPOINT_RESTORE
+	REGSET_PACA_KEYS,
+	REGSET_PACG_KEYS,
+#endif
 #endif
 };
 
@@ -1074,6 +1200,24 @@  static const struct user_regset aarch64_regsets[] = {
 		.get = pac_mask_get,
 		/* this cannot be set dynamically */
 	},
+#ifdef CONFIG_CHECKPOINT_RESTORE
+	[REGSET_PACA_KEYS] = {
+		.core_note_type = NT_ARM_PACA_KEYS,
+		.n = sizeof(struct user_pac_address_keys) / sizeof(u64),
+		.size = sizeof(u64),
+		.align = sizeof(u64),
+		.get = pac_address_keys_get,
+		.set = pac_address_keys_set,
+	},
+	[REGSET_PACG_KEYS] = {
+		.core_note_type = NT_ARM_PACG_KEYS,
+		.n = sizeof(struct user_pac_generic_keys) / sizeof(u64),
+		.size = sizeof(u64),
+		.align = sizeof(u64),
+		.get = pac_generic_keys_get,
+		.set = pac_generic_keys_set,
+	},
+#endif
 #endif
 };
 
diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
index e4d6ddd93567..34c02e4290fe 100644
--- a/include/uapi/linux/elf.h
+++ b/include/uapi/linux/elf.h
@@ -421,6 +421,8 @@  typedef struct elf64_shdr {
 #define NT_ARM_SYSTEM_CALL	0x404	/* ARM system call number */
 #define NT_ARM_SVE	0x405		/* ARM Scalable Vector Extension registers */
 #define NT_ARM_PAC_MASK		0x406	/* ARM pointer authentication code masks */
+#define NT_ARM_PACA_KEYS	0x407	/* ARM pointer authentication address keys */
+#define NT_ARM_PACG_KEYS	0x408	/* ARM pointer authentication generic key */
 #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 */