Re: [PATCH v29 26/32] x86/cet/shstk: Introduce shadow stack token setup/verify routines

From: Borislav Petkov
Date: Thu Aug 26 2021 - 13:21:18 EST


On Fri, Aug 20, 2021 at 11:11:55AM -0700, Yu-cheng Yu wrote:
> A shadow stack restore token marks a restore point of the shadow stack, and
> the address in a token must point directly above the token, which is within
> the same shadow stack. This is distinctively different from other pointers
> on the shadow stack, since those pointers point to executable code area.
>
> The restore token can be used as an extra protection for signal handling.
> To deliver a signal, create a shadow stack restore token and put the token
> and the signal restorer address on the shadow stack. In sigreturn, verify
> the token and restore from it the shadow stack pointer.

I guess this all bla about signals needs to go now too...

> Introduce token setup and verify routines. Also introduce WRUSS, which is
> a kernel-mode instruction but writes directly to user shadow stack. It is
> used to construct user signal stack as described above.
>
> Signed-off-by: Yu-cheng Yu <yu-cheng.yu@xxxxxxxxx>
> Cc: Kees Cook <keescook@xxxxxxxxxxxx>

...

> diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c
> index 7c1ca2476a5e..548d0552f9b3 100644
> --- a/arch/x86/kernel/shstk.c
> +++ b/arch/x86/kernel/shstk.c
> @@ -20,6 +20,7 @@
> #include <asm/fpu/xstate.h>
> #include <asm/fpu/types.h>
> #include <asm/cet.h>
> +#include <asm/special_insns.h>
>
> static void start_update_msrs(void)
> {
> @@ -193,3 +194,142 @@ void shstk_disable(void)
>
> shstk_free(current);
> }
> +
> +static unsigned long get_user_shstk_addr(void)
> +{
> + struct fpu *fpu = &current->thread.fpu;
> + unsigned long ssp = 0;

Unneeded variable init.

> +
> + fpregs_lock();
> +
> + if (fpregs_state_valid(fpu, smp_processor_id())) {
> + rdmsrl(MSR_IA32_PL3_SSP, ssp);
> + } else {
> + struct cet_user_state *p;
> +
> + /*
> + * When !fpregs_state_valid() and get_xsave_addr() returns

What does "!fpregs_state_valid()" mean in English?

> + * null, XFEAUTRE_CET_USER is in init state. Shadow stack

XFEATURE_CET_USER

> + * pointer is null in this case, so return zero. This can
> + * happen when shadow stack is enabled, but its xstates in

s/its xstates/the shadow stack component/

> + * memory is corrupted.
> + */
> + p = get_xsave_addr(&fpu->state.xsave, XFEATURE_CET_USER);
> + if (p)
> + ssp = p->user_ssp;
else
ssp = 0;

and this way it is absolutely unambiguous what the comment says.

> + }
> +
> + fpregs_unlock();
> +
> + return ssp;
> +}
> +
> +/*
> + * Create a restore token on the shadow stack. A token is always 8-byte
> + * and aligned to 8.
> + */
> +static int create_rstor_token(bool ia32, unsigned long ssp,

s/ia32/proc32/g

> + unsigned long *token_addr)
> +{
> + unsigned long addr;
> +
> + /* Aligned to 8 is aligned to 4, so test 8 first */
> + if ((!ia32 && !IS_ALIGNED(ssp, 8)) || !IS_ALIGNED(ssp, 4))
> + return -EINVAL;
> +
> + addr = ALIGN_DOWN(ssp, 8) - 8;
> +
> + /* Is the token for 64-bit? */
> + if (!ia32)
> + ssp |= BIT(0);
> +
> + if (write_user_shstk_64((u64 __user *)addr, (u64)ssp))
> + return -EFAULT;
> +
> + *token_addr = addr;
> +
> + return 0;
> +}

...

> +/*
> + * Verify token_addr points to a valid token, and then set *new_ssp

"Verify the user shadow stack has a valid token on it, ... "

> + * according to the token.
> + */
> +int shstk_check_rstor_token(bool proc32, unsigned long *new_ssp)
> +{
> + unsigned long token_addr;
> + unsigned long token;
> + bool shstk32;
> +
> + token_addr = get_user_shstk_addr();

if (!token_addr)
return -EINVAL;

> +
> + if (get_user(token, (unsigned long __user *)token_addr))
> + return -EFAULT;
> +
> + /* Is mode flag correct? */
> + shstk32 = !(token & BIT(0));
> + if (proc32 ^ shstk32)
> + return -EINVAL;
> +
> + /* Is busy flag set? */
> + if (token & BIT(1))
> + return -EINVAL;
> +
> + /* Mask out flags */
> + token &= ~3UL;
> +
> + /*
> + * Restore address aligned?
> + */

Single line comment works too:

/* Restore address aligned? */

> + if ((!proc32 && !IS_ALIGNED(token, 8)) || !IS_ALIGNED(token, 4))
> + return -EINVAL;
> +
> + /*
> + * Token placed properly?
> + */

Ditto.

> + if (((ALIGN_DOWN(token, 8) - 8) != token_addr) || token >= TASK_SIZE_MAX)
> + return -EINVAL;
> +
> + *new_ssp = token;
> +
> + return 0;
> +}
> --
> 2.21.0
>

--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette