Re: [PATCH] kernel: sys: fix potential Spectre v1

From: Mark Rutland
Date: Wed May 23 2018 - 10:14:23 EST


On Wed, May 23, 2018 at 11:08:40AM +0200, Peter Zijlstra wrote:
>
> Sorry for being late to the party..

Likewise!

> On Wed, May 23, 2018 at 12:03:57AM -0500, Gustavo A. R. Silva wrote:
> > +#define validate_index_nospec(index, size) \
> > +({ \
> > + bool ret = true; \
> > + typeof(index) *ptr = &(index); \
> > + typeof(size) _s = (size); \
> > + \
> > + BUILD_BUG_ON(sizeof(*ptr) > sizeof(long)); \
> > + BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \
> > + \
> > + if (*ptr >= size) \
> > + ret = false; \
> > + \
> > + *ptr = array_index_nospec(*ptr, _s); \
> > + \
> > + ret; \
> > +})
>
> Would not something like:
>
> bool ret = false;
>
> ....
>
> if (*ptr < _s) {
> *ptr = array_index_nospec(*ptr, _s);
> ret = true;
> }
>
> ret;
>
> be more obvious?

I think that either way, we have a potential problem if the compiler
generates a branch dependent on the result of validate_index_nospec().

In that case, we could end up with codegen approximating:

bool safe = false;

if (idx < bound) {
idx = array_index_nospec(idx, bound);
safe = true;
}

// this branch can be mispredicted
if (safe) {
foo = array[idx];
}

... and thus we lose the nospec protection.

I also suspect that compiler transformations mean that this might
already be the case for patterns like:

if (idx < bound) {
safe_idx = array_index_nospec(idx, bound)];
...
foo = array[safe_idx];
}

... if the compiler can transform that to something like:

if (idx < bound) {
idx = array_index_nospec(idx, bound);
}

// can be mispredicted
if (idx < bound) {
foo = array[idx];
}

... which I think a compiler might be capable of, depending on the rest
of the function body (e.g. if there's a common portion shared with the
else case).

I'll see if I can trigger that in a test case. :/

Thanks,
Mark.