Re: [PATCH v7 02/39] prctl: arch-agnostic prctl for shadow stack

From: Deepak Gupta
Date: Tue Dec 12 2023 - 14:17:30 EST


On Wed, Nov 22, 2023 at 1:43 AM Mark Brown <broonie@xxxxxxxxxx> wrote:
>
> Three architectures (x86, aarch64, riscv) have announced support for
> shadow stacks with fairly similar functionality. While x86 is using
> arch_prctl() to control the functionality neither arm64 nor riscv uses
> that interface so this patch adds arch-agnostic prctl() support to
> get and set status of shadow stacks and lock the current configuation to
> prevent further changes, with support for turning on and off individual
> subfeatures so applications can limit their exposure to features that
> they do not need. The features are:
>
> - PR_SHADOW_STACK_ENABLE: Tracking and enforcement of shadow stacks,
> including allocation of a shadow stack if one is not already
> allocated.
> - PR_SHADOW_STACK_WRITE: Writes to specific addresses in the shadow
> stack.
> - PR_SHADOW_STACK_PUSH: Push additional values onto the shadow stack.
>
> These features are expected to be inherited by new threads and cleared
> on exec(), unknown features should be rejected for enable but accepted
> for locking (in order to allow for future proofing).
>
> This is based on a patch originally written by Deepak Gupta but modified
> fairly heavily, support for indirect landing pads is removed, additional
> modes added and the locking interface reworked. The set status prctl()
> is also reworked to just set flags, if setting/reading the shadow stack
> pointer is required this could be a separate prctl.
>
> Signed-off-by: Mark Brown <broonie@xxxxxxxxxx>
> ---
> include/linux/mm.h | 4 ++++
> include/uapi/linux/prctl.h | 22 ++++++++++++++++++++++
> kernel/sys.c | 30 ++++++++++++++++++++++++++++++
> 3 files changed, 56 insertions(+)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 10462f354614..8b28483b4afa 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -4143,4 +4143,8 @@ static inline bool pfn_is_unaccepted_memory(unsigned long pfn)
> return range_contains_unaccepted_memory(paddr, paddr + PAGE_SIZE);
> }
>
> +int arch_get_shadow_stack_status(struct task_struct *t, unsigned long __user *status);
> +int arch_set_shadow_stack_status(struct task_struct *t, unsigned long status);
> +int arch_lock_shadow_stack_status(struct task_struct *t, unsigned long status);
> +
> #endif /* _LINUX_MM_H */
> diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
> index 370ed14b1ae0..3c66ed8f46d8 100644
> --- a/include/uapi/linux/prctl.h
> +++ b/include/uapi/linux/prctl.h
> @@ -306,4 +306,26 @@ struct prctl_mm_map {
> # define PR_RISCV_V_VSTATE_CTRL_NEXT_MASK 0xc
> # define PR_RISCV_V_VSTATE_CTRL_MASK 0x1f
>
> +/*
> + * Get the current shadow stack configuration for the current thread,
> + * this will be the value configured via PR_SET_SHADOW_STACK_STATUS.
> + */
> +#define PR_GET_SHADOW_STACK_STATUS 71
> +
> +/*
> + * Set the current shadow stack configuration. Enabling the shadow
> + * stack will cause a shadow stack to be allocated for the thread.
> + */
> +#define PR_SET_SHADOW_STACK_STATUS 72
> +# define PR_SHADOW_STACK_ENABLE (1UL << 0)

Other architecture may require disabling shadow stack if glibc
tunables is set to permissive mode.
In permissive mode, if glibc encounters `dlopen` on an object which
doesn't support shadow stack,
glibc should be able to issue PR_SHADOW_STACK_DISABLE.

Architectures can choose to implement or not but I think arch agnostic
code should enumerate this.

> +# define PR_SHADOW_STACK_WRITE (1UL << 1)
> +# define PR_SHADOW_STACK_PUSH (1UL << 2)
> +
> +/*
> + * Prevent further changes to the specified shadow stack
> + * configuration. All bits may be locked via this call, including
> + * undefined bits.
> + */