diff mbox series

[v3] arm64: add ptrace regsets for ptrauth key management

Message ID 20190123153519.7874-1-kristina.martsenko@arm.com (mailing list archive)
State New, archived
Headers show
Series [v3] arm64: add ptrace regsets for ptrauth key management | expand

Commit Message

Kristina Martšenko Jan. 23, 2019, 3:35 p.m. UTC
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 only intended use case is
checkpointing and restoring processes that are using pointer
authentication. (This can be changed later if there are other use
cases.)

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

Changes in v3:
 - 128-bit keys in uapi struct, conversions to/from ptrauth_keys fields
 - Commit message tweaks

 Documentation/arm64/pointer-authentication.txt |   5 +
 arch/arm64/include/uapi/asm/ptrace.h           |  13 +++
 arch/arm64/kernel/ptrace.c                     | 139 +++++++++++++++++++++++++
 include/uapi/linux/elf.h                       |   2 +
 4 files changed, 159 insertions(+)

Comments

Dave Martin Jan. 23, 2019, 5:37 p.m. UTC | #1
On Wed, Jan 23, 2019 at 03:35:19PM +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.

Is there a use case for including these in the coredump?  Perhaps it
would make sense to suppress them.  If the core code doesn't make that
easy it may not be worth it though.

Including the keys in a coredump doesn't feel like a security risk,
unless the checkpoint restore folks have a way of resurrecting dead
processes from coredumps.  Otherwise, (statistically) no new process
will have the same keys anyway.

> The regsets are only exposed if the kernel is compiled with
> CONFIG_CHECKPOINT_RESTORE=y, as the only intended use case is
> checkpointing and restoring processes that are using pointer
> authentication. (This can be changed later if there are other use
> cases.)
> 
> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>

I think this looks nicer overall.

A few comments, which are all nits except for the questions about the
memset() calls.

> ---
> 
> Changes in v3:
>  - 128-bit keys in uapi struct, conversions to/from ptrauth_keys fields
>  - Commit message tweaks
> 
>  Documentation/arm64/pointer-authentication.txt |   5 +
>  arch/arm64/include/uapi/asm/ptrace.h           |  13 +++
>  arch/arm64/kernel/ptrace.c                     | 139 +++++++++++++++++++++++++
>  include/uapi/linux/elf.h                       |   2 +
>  4 files changed, 159 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..d78623acb649 100644
> --- a/arch/arm64/include/uapi/asm/ptrace.h
> +++ b/arch/arm64/include/uapi/asm/ptrace.h
> @@ -233,6 +233,19 @@ struct user_pac_mask {
>  	__u64		insn_mask;
>  };
>  
> +/* pointer authentication keys (NT_ARM_PACA_KEYS, NT_ARM_PACG_KEYS) */
> +
> +struct user_pac_address_keys {
> +	__uint128_t	apiakey;
> +	__uint128_t	apibkey;
> +	__uint128_t	apdakey;
> +	__uint128_t	apdbkey;
> +};
> +
> +struct user_pac_generic_keys {
> +	__uint128_t	apgakey;
> +};
> +
>  #endif /* __ASSEMBLY__ */
>  
>  #endif /* _UAPI__ASM_PTRACE_H */
> diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> index 9dce33b0e260..102f71fe0288 100644
> --- a/arch/arm64/kernel/ptrace.c
> +++ b/arch/arm64/kernel/ptrace.c
> @@ -979,6 +979,123 @@ 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)

const?

> +{
> +	ukeys->apiakey = ((__uint128_t)keys->apia.hi << 64) | keys->apia.lo;

We could have a helper

static __uint128_t key_to_user(struct ptrauth_key const *key)
{
	return (__uint128_t)key->hi << 64 | key->lo;
}

and then do

	ukeys->apiakey = key_to_user(&keys->apia);
	ukeys->apibkey = key_to_user(&keys->apib);

> +	ukeys->apibkey = ((__uint128_t)keys->apib.hi << 64) | keys->apib.lo;
> +	ukeys->apdakey = ((__uint128_t)keys->apda.hi << 64) | keys->apda.lo;
> +	ukeys->apdbkey = ((__uint128_t)keys->apdb.hi << 64) | keys->apdb.lo;
> +}
> +
> +static void pac_address_keys_from_user(struct user_pac_address_keys *ukeys,

const?

> +				       struct ptrauth_keys *keys)

Is the argument order for this function intentional?

It means that for pac_address_keys_to_user() the destination argument is
on the left, but in pac_address_keys_from_user() the destination
argument is on the right.

This is mildly confusing, but because the arguments are of different
types the compiler should catch mistakes (especially if the const is
added appropriately.)

If this is following a pattern already set elsewhere, then fair enough.

> +{
> +	keys->apia.lo = (unsigned long)ukeys->apiakey;
> +	keys->apia.hi = (unsigned long)(ukeys->apiakey >> 64);

Similarly, we could have 

static struct ptrauth_key key_from_user(__uint128_t ukey)
{
	struct ptrauth_key key = {
		.lo = (unsigned long)ukey;
		.hi = (unsigned long)(ukey >> 64);
	};

	return key;
}

	keys->apia = key_from_user(ukeys->apiakey);
	keys->apib = key_from_user(ukeys->apibkey);

These factorings may be overkill though.

> +	keys->apib.lo = (unsigned long)ukeys->apibkey;
> +	keys->apib.hi = (unsigned long)(ukeys->apibkey >> 64);
> +	keys->apda.lo = (unsigned long)ukeys->apdakey;
> +	keys->apda.hi = (unsigned long)(ukeys->apdakey >> 64);
> +	keys->apdb.lo = (unsigned long)ukeys->apdbkey;
> +	keys->apdb.hi = (unsigned long)(ukeys->apdbkey >> 64);
> +}
> +
> +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));

Why this memset?

(In particular, we have no similar memset in pac_address_keys_get().
Maybe it's needed for some reason I've missed, but I don't see it.)

> +	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)

const?

> +{
> +	ukeys->apgakey = ((__uint128_t)keys->apga.hi << 64) | keys->apga.lo;
> +}
> +
> +static void pac_generic_keys_from_user(struct user_pac_generic_keys *ukeys,

const?

> +				       struct ptrauth_keys *keys)
> +{
> +	keys->apga.lo = (unsigned long)ukeys->apgakey;
> +	keys->apga.hi = (unsigned long)(ukeys->apgakey >> 64);

(The above helpers could be used here too.)

> +}
> +
> +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));

Similarly to above, is this needed?

> +	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;
> +}

[...]

Cheers
---Dave
Kristina Martšenko Jan. 29, 2019, 6:44 p.m. UTC | #2
On 23/01/2019 17:37, Dave Martin wrote:
> On Wed, Jan 23, 2019 at 03:35:19PM +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.
> 
> Is there a use case for including these in the coredump?  Perhaps it
> would make sense to suppress them.  If the core code doesn't make that
> easy it may not be worth it though.

From what I can see there is no proper way to exclude the keys from the
core dump. There is an ->active() callback on struct user_regset, which
could be implemented to always return 0, which would exclude the keys
from the core dump. But while the callback seems to currently only be
used for the core dump, its purpose seems to be more general, so I don't
think it would be right to have it always return 0.

> Including the keys in a coredump doesn't feel like a security risk,
> unless the checkpoint restore folks have a way of resurrecting dead
> processes from coredumps.  Otherwise, (statistically) no new process
> will have the same keys anyway.
> 
>> The regsets are only exposed if the kernel is compiled with
>> CONFIG_CHECKPOINT_RESTORE=y, as the only intended use case is
>> checkpointing and restoring processes that are using pointer
>> authentication. (This can be changed later if there are other use
>> cases.)
>>
>> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
> 
> I think this looks nicer overall.
> 
> A few comments, which are all nits except for the questions about the
> memset() calls.
> 
>> ---
>>
>> Changes in v3:
>>  - 128-bit keys in uapi struct, conversions to/from ptrauth_keys fields
>>  - Commit message tweaks
>>
>>  Documentation/arm64/pointer-authentication.txt |   5 +
>>  arch/arm64/include/uapi/asm/ptrace.h           |  13 +++
>>  arch/arm64/kernel/ptrace.c                     | 139 +++++++++++++++++++++++++
>>  include/uapi/linux/elf.h                       |   2 +
>>  4 files changed, 159 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..d78623acb649 100644
>> --- a/arch/arm64/include/uapi/asm/ptrace.h
>> +++ b/arch/arm64/include/uapi/asm/ptrace.h
>> @@ -233,6 +233,19 @@ struct user_pac_mask {
>>  	__u64		insn_mask;
>>  };
>>  
>> +/* pointer authentication keys (NT_ARM_PACA_KEYS, NT_ARM_PACG_KEYS) */
>> +
>> +struct user_pac_address_keys {
>> +	__uint128_t	apiakey;
>> +	__uint128_t	apibkey;
>> +	__uint128_t	apdakey;
>> +	__uint128_t	apdbkey;
>> +};
>> +
>> +struct user_pac_generic_keys {
>> +	__uint128_t	apgakey;
>> +};
>> +
>>  #endif /* __ASSEMBLY__ */
>>  
>>  #endif /* _UAPI__ASM_PTRACE_H */
>> diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
>> index 9dce33b0e260..102f71fe0288 100644
>> --- a/arch/arm64/kernel/ptrace.c
>> +++ b/arch/arm64/kernel/ptrace.c
>> @@ -979,6 +979,123 @@ 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)
> 
> const?

Done.

>> +{
>> +	ukeys->apiakey = ((__uint128_t)keys->apia.hi << 64) | keys->apia.lo;
> 
> We could have a helper
> 
> static __uint128_t key_to_user(struct ptrauth_key const *key)
> {
> 	return (__uint128_t)key->hi << 64 | key->lo;
> }
> 
> and then do
> 
> 	ukeys->apiakey = key_to_user(&keys->apia);
> 	ukeys->apibkey = key_to_user(&keys->apib);

Done.

>> +	ukeys->apibkey = ((__uint128_t)keys->apib.hi << 64) | keys->apib.lo;
>> +	ukeys->apdakey = ((__uint128_t)keys->apda.hi << 64) | keys->apda.lo;
>> +	ukeys->apdbkey = ((__uint128_t)keys->apdb.hi << 64) | keys->apdb.lo;
>> +}
>> +
>> +static void pac_address_keys_from_user(struct user_pac_address_keys *ukeys,
> 
> const?

Done.

>> +				       struct ptrauth_keys *keys)
> 
> Is the argument order for this function intentional?
> 
> It means that for pac_address_keys_to_user() the destination argument is
> on the left, but in pac_address_keys_from_user() the destination
> argument is on the right.
> 
> This is mildly confusing, but because the arguments are of different
> types the compiler should catch mistakes (especially if the const is
> added appropriately.)
> 
> If this is following a pattern already set elsewhere, then fair enough.

I think the order is accidental here. I'll move the destination argument
to the left.

>> +{
>> +	keys->apia.lo = (unsigned long)ukeys->apiakey;
>> +	keys->apia.hi = (unsigned long)(ukeys->apiakey >> 64);
> 
> Similarly, we could have 
> 
> static struct ptrauth_key key_from_user(__uint128_t ukey)
> {
> 	struct ptrauth_key key = {
> 		.lo = (unsigned long)ukey;
> 		.hi = (unsigned long)(ukey >> 64);
> 	};
> 
> 	return key;
> }
> 
> 	keys->apia = key_from_user(ukeys->apiakey);
> 	keys->apib = key_from_user(ukeys->apibkey);
> 
> These factorings may be overkill though.

Done.

>> +	keys->apib.lo = (unsigned long)ukeys->apibkey;
>> +	keys->apib.hi = (unsigned long)(ukeys->apibkey >> 64);
>> +	keys->apda.lo = (unsigned long)ukeys->apdakey;
>> +	keys->apda.hi = (unsigned long)(ukeys->apdakey >> 64);
>> +	keys->apdb.lo = (unsigned long)ukeys->apdbkey;
>> +	keys->apdb.hi = (unsigned long)(ukeys->apdbkey >> 64);
>> +}
>> +
>> +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));
> 
> Why this memset?

From your previous email [1], I understood that you wanted the user
struct zeroed to avoid leaking kernel stack to userspace through padding
bits, even if the keys are 128-bit (or 64-bit) and there shouldn't be
any padding bits anyway. I don't personally think that the struct needs
to be zeroed (since there shouldn't be any padding), so if it's alright
with you I'll remove the memset.

[1] https://lore.kernel.org/lkml/20181212152347.GY3505@e103592.cambridge.arm.com/

> (In particular, we have no similar memset in pac_address_keys_get().

I guess you mean pac_address_keys_set. We only copy the fields into the
kernel struct there, not the padding, so this issue doesn't apply.

> Maybe it's needed for some reason I've missed, but I don't see it.)
> 
>> +	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)
> 
> const?

Done.

>> +{
>> +	ukeys->apgakey = ((__uint128_t)keys->apga.hi << 64) | keys->apga.lo;
>> +}
>> +
>> +static void pac_generic_keys_from_user(struct user_pac_generic_keys *ukeys,
> 
> const?

Done.

>> +				       struct ptrauth_keys *keys)
>> +{
>> +	keys->apga.lo = (unsigned long)ukeys->apgakey;
>> +	keys->apga.hi = (unsigned long)(ukeys->apgakey >> 64);
> 
> (The above helpers could be used here too.)
> 
>> +}
>> +
>> +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));
> 
> Similarly to above, is this needed?

Same reason as above; I'll remove it.

Kristina

>> +	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;
>> +}
Dave Martin Jan. 29, 2019, 8:48 p.m. UTC | #3
On Tue, Jan 29, 2019 at 06:44:38PM +0000, Kristina Martsenko wrote:
> On 23/01/2019 17:37, Dave Martin wrote:
> > On Wed, Jan 23, 2019 at 03:35:19PM +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.
> > 
> > Is there a use case for including these in the coredump?  Perhaps it
> > would make sense to suppress them.  If the core code doesn't make that
> > easy it may not be worth it though.
> 
> From what I can see there is no proper way to exclude the keys from the
> core dump. There is an ->active() callback on struct user_regset, which
> could be implemented to always return 0, which would exclude the keys
> from the core dump. But while the callback seems to currently only be
> used for the core dump, its purpose seems to be more general, so I don't
> think it would be right to have it always return 0.

OK, if there's no straightforward answer here, I suggest we leave it
as-is, especially if nothing else handles this specially.

[...]

> >> diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
> >> index 9dce33b0e260..102f71fe0288 100644
> >> --- a/arch/arm64/kernel/ptrace.c
> >> +++ b/arch/arm64/kernel/ptrace.c
> >> @@ -979,6 +979,123 @@ 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)
> > 
> > const?
> 
> Done.
> 
> >> +{
> >> +	ukeys->apiakey = ((__uint128_t)keys->apia.hi << 64) | keys->apia.lo;
> > 
> > We could have a helper
> > 
> > static __uint128_t key_to_user(struct ptrauth_key const *key)
> > {
> > 	return (__uint128_t)key->hi << 64 | key->lo;
> > }
> > 
> > and then do
> > 
> > 	ukeys->apiakey = key_to_user(&keys->apia);
> > 	ukeys->apibkey = key_to_user(&keys->apib);
> 
> Done.
> 
> >> +	ukeys->apibkey = ((__uint128_t)keys->apib.hi << 64) | keys->apib.lo;
> >> +	ukeys->apdakey = ((__uint128_t)keys->apda.hi << 64) | keys->apda.lo;
> >> +	ukeys->apdbkey = ((__uint128_t)keys->apdb.hi << 64) | keys->apdb.lo;
> >> +}
> >> +
> >> +static void pac_address_keys_from_user(struct user_pac_address_keys *ukeys,
> > 
> > const?
> 
> Done.
> 
> >> +				       struct ptrauth_keys *keys)
> > 
> > Is the argument order for this function intentional?
> > 
> > It means that for pac_address_keys_to_user() the destination argument is
> > on the left, but in pac_address_keys_from_user() the destination
> > argument is on the right.
> > 
> > This is mildly confusing, but because the arguments are of different
> > types the compiler should catch mistakes (especially if the const is
> > added appropriately.)
> > 
> > If this is following a pattern already set elsewhere, then fair enough.
> 
> I think the order is accidental here. I'll move the destination argument
> to the left.

OK, that sounds fine.

> >> +{
> >> +	keys->apia.lo = (unsigned long)ukeys->apiakey;
> >> +	keys->apia.hi = (unsigned long)(ukeys->apiakey >> 64);
> > 
> > Similarly, we could have 
> > 
> > static struct ptrauth_key key_from_user(__uint128_t ukey)
> > {
> > 	struct ptrauth_key key = {
> > 		.lo = (unsigned long)ukey;
> > 		.hi = (unsigned long)(ukey >> 64);
> > 	};
> > 
> > 	return key;
> > }
> > 
> > 	keys->apia = key_from_user(ukeys->apiakey);
> > 	keys->apib = key_from_user(ukeys->apibkey);
> > 
> > These factorings may be overkill though.
> 
> Done.

OK, fair enough.

> >> +	keys->apib.lo = (unsigned long)ukeys->apibkey;
> >> +	keys->apib.hi = (unsigned long)(ukeys->apibkey >> 64);
> >> +	keys->apda.lo = (unsigned long)ukeys->apdakey;
> >> +	keys->apda.hi = (unsigned long)(ukeys->apdakey >> 64);
> >> +	keys->apdb.lo = (unsigned long)ukeys->apdbkey;
> >> +	keys->apdb.hi = (unsigned long)(ukeys->apdbkey >> 64);
> >> +}
> >> +
> >> +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));
> > 
> > Why this memset?
> 
> From your previous email [1], I understood that you wanted the user
> struct zeroed to avoid leaking kernel stack to userspace through padding
> bits, even if the keys are 128-bit (or 64-bit) and there shouldn't be
> any padding bits anyway. I don't personally think that the struct needs
> to be zeroed (since there shouldn't be any padding), so if it's alright
> with you I'll remove the memset.
> 
> [1] https://lore.kernel.org/lkml/20181212152347.GY3505@e103592.cambridge.arm.com/
> 
> > (In particular, we have no similar memset in pac_address_keys_get().
> 
> I guess you mean pac_address_keys_set. We only copy the fields into the

Ah yes, sorry.

> kernel struct there, not the padding, so this issue doesn't apply.

I could have explained myself better.

C allows padding to be populated with arbitrary data any time a member
of the struct is written, so there's really no option but to ensure that
the struct contains no padding at all.

Since the affected structs are homogeneous we should be OK here, and
explicitly computing and assiging appropriate values rather than doing
a straight copy minimises the possiblility for unexpected things
happening, as does forbidding half-keys from being modified by
userspace.

So I agree, the memset() doesn't look like it's adding anything and
it can go.

> > Maybe it's needed for some reason I've missed, but I don't see it.)
> > 
> >> +	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)
> > 
> > const?
> 
> Done.
> 
> >> +{
> >> +	ukeys->apgakey = ((__uint128_t)keys->apga.hi << 64) | keys->apga.lo;
> >> +}
> >> +
> >> +static void pac_generic_keys_from_user(struct user_pac_generic_keys *ukeys,
> > 
> > const?
> 
> Done.
> 
> >> +				       struct ptrauth_keys *keys)
> >> +{
> >> +	keys->apga.lo = (unsigned long)ukeys->apgakey;
> >> +	keys->apga.hi = (unsigned long)(ukeys->apgakey >> 64);
> > 
> > (The above helpers could be used here too.)
> > 
> >> +}
> >> +
> >> +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));
> > 
> > Similarly to above, is this needed?
> 
> Same reason as above; I'll remove it.
> 
> Kristina

[...]

OK, thanks
---Dave
diff mbox series

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..d78623acb649 100644
--- a/arch/arm64/include/uapi/asm/ptrace.h
+++ b/arch/arm64/include/uapi/asm/ptrace.h
@@ -233,6 +233,19 @@  struct user_pac_mask {
 	__u64		insn_mask;
 };
 
+/* pointer authentication keys (NT_ARM_PACA_KEYS, NT_ARM_PACG_KEYS) */
+
+struct user_pac_address_keys {
+	__uint128_t	apiakey;
+	__uint128_t	apibkey;
+	__uint128_t	apdakey;
+	__uint128_t	apdbkey;
+};
+
+struct user_pac_generic_keys {
+	__uint128_t	apgakey;
+};
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* _UAPI__ASM_PTRACE_H */
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 9dce33b0e260..102f71fe0288 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -979,6 +979,123 @@  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 = ((__uint128_t)keys->apia.hi << 64) | keys->apia.lo;
+	ukeys->apibkey = ((__uint128_t)keys->apib.hi << 64) | keys->apib.lo;
+	ukeys->apdakey = ((__uint128_t)keys->apda.hi << 64) | keys->apda.lo;
+	ukeys->apdbkey = ((__uint128_t)keys->apdb.hi << 64) | keys->apdb.lo;
+}
+
+static void pac_address_keys_from_user(struct user_pac_address_keys *ukeys,
+				       struct ptrauth_keys *keys)
+{
+	keys->apia.lo = (unsigned long)ukeys->apiakey;
+	keys->apia.hi = (unsigned long)(ukeys->apiakey >> 64);
+	keys->apib.lo = (unsigned long)ukeys->apibkey;
+	keys->apib.hi = (unsigned long)(ukeys->apibkey >> 64);
+	keys->apda.lo = (unsigned long)ukeys->apdakey;
+	keys->apda.hi = (unsigned long)(ukeys->apdakey >> 64);
+	keys->apdb.lo = (unsigned long)ukeys->apdbkey;
+	keys->apdb.hi = (unsigned long)(ukeys->apdbkey >> 64);
+}
+
+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 = ((__uint128_t)keys->apga.hi << 64) | keys->apga.lo;
+}
+
+static void pac_generic_keys_from_user(struct user_pac_generic_keys *ukeys,
+				       struct ptrauth_keys *keys)
+{
+	keys->apga.lo = (unsigned long)ukeys->apgakey;
+	keys->apga.hi = (unsigned long)(ukeys->apgakey >> 64);
+}
+
+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 +1112,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 +1195,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(__uint128_t),
+		.size = sizeof(__uint128_t),
+		.align = sizeof(__uint128_t),
+		.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(__uint128_t),
+		.size = sizeof(__uint128_t),
+		.align = sizeof(__uint128_t),
+		.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 */