[PATCH] powerpc/kernel: Fix potential spectre v1 in syscall

Nathan Lynch nathanl at linux.ibm.com
Tue Mar 19 02:25:53 AEDT 2024


Michael Ellerman <mpe at ellerman.id.au> writes:

> Breno Leitao <leitao at debian.org> writes:
>> On Tue, Mar 12, 2024 at 08:17:42AM +0000, Christophe Leroy wrote:
>>> +Nathan as this is RTAS related.

Thanks!

>>> Le 21/08/2018 à 20:42, Breno Leitao a écrit :
>>> > The rtas syscall reads a value from a user-provided structure and uses it
>>> > to index an array, being a possible area for a potential spectre v1 attack.
>>> > This is the code that exposes this problem.
>>> > 
>>> > 	args.rets = &args.args[nargs];
>>> > 
>>> > The nargs is an user provided value, and the below code is an example where
>>> > the 'nargs' value would be set to XX.
>>> > 
>>> > 	struct rtas_args ra;
>>> > 	ra.nargs = htobe32(XX);
>>> > 	syscall(__NR_rtas, &ra);
>>> 
>>> 
>>> This patch has been hanging around in patchwork since 2018 and doesn't 
>>> apply anymore. Is it still relevant ? If so, can you rebase et resubmit ?
>>
>> This seems to be important, since nargs is a user-provided value. I can
>> submit it if the maintainers are willing to accept. I do not want to
>> spend my time if no one is willing to review it.
>
> My memory is that I didn't think it was actually a problem, because all
> we do is memset args.rets to zero.

This is also my initial reaction to this. I suppose if the memset()
implementation performs some validation of the destination buffer
contents (comparing to a known poison value or something) that could
load the CPU cache then there is a more plausible issue?

> Anyway we should probably just fix it to be safe and keep the static
> checkers happy.

Here is the relevant passage in its current state:

        if (copy_from_user(&args, uargs, 3 * sizeof(u32)) != 0)
                return -EFAULT;

        nargs = be32_to_cpu(args.nargs);
        nret  = be32_to_cpu(args.nret);
        token = be32_to_cpu(args.token);

        if (nargs >= ARRAY_SIZE(args.args)
            || nret > ARRAY_SIZE(args.args)
            || nargs + nret > ARRAY_SIZE(args.args))
                return -EINVAL;

        /* Copy in args. */
        if (copy_from_user(args.args, uargs->args,
                           nargs * sizeof(rtas_arg_t)) != 0)
                return -EFAULT;

        /*
         * If this token doesn't correspond to a function the kernel
         * understands, you're not allowed to call it.
         */
        func = rtas_token_to_function_untrusted(token);
        if (!func)
                return -EINVAL;

        args.rets = &args.args[nargs];
        memset(args.rets, 0, nret * sizeof(rtas_arg_t));

Some questions:

1. The patch sanitizes 'nargs' immediately before the call to memset(),
   but shouldn't that happen before 'nargs' is used as an input to
   copy_from_user()?

2. If 'nargs' needs this treatment, then why wouldn't the user-supplied
   'nret' and 'token' need them as well? 'nret' is used to index the
   same array as 'nargs'. And at least conceptually, 'token' is used to
   index a data structure (xarray) with array-like semantics (to be
   fair, this is a relatively recent development and was not the case
   when this change was submitted).


More information about the Linuxppc-dev mailing list