|
|
Subscribe / Log in / New account

Spectre V1 defense in GCC

Spectre V1 defense in GCC

Posted Jul 10, 2018 23:50 UTC (Tue) by lambda (subscriber, #40735)
In reply to: Spectre V1 defense in GCC by Sesse
Parent article: Spectre V1 defense in GCC

The docs part of the patches provide a better description of the actual behavior. It always returns the given value when executing non-speculatively. When executing speculatively, it either blocks until all speculation is resolved, or if the architecture supports it, returns the fallback value if there is outstanding speculation that could turn out to be incorrect. This basically means that in the speculative scenarios that could cause issues, you will just work with the dummy value, and the speculation will be useless but safe.

+(speculation_safe_value,
+"This target hook can be used to generate a target-specific code\n\
+ sequence that implements the @code{__builtin_speculation_safe_value}\n\
+ built-in function.  The function must always return @var{val} in\n\
+ @var{result} in mode @var{mode} when the cpu is not executing\n\
+ speculatively, but must never return that when speculating until it\n\
+ is known that the speculation will not be unwound.  The hook supports\n\
+ two primary mechanisms for implementing the requirements.  The first\n\
+ is to emit a speculation barrier which forces the processor to wait\n\
+ until all prior speculative operations have been resolved; the second\n\
+ is to use a target-specific mechanism that can track the speculation\n\
+ state and to return @var{failval} if it can determine that\n\
+ speculation must be unwound at a later time.\n\
+ \n\
+ The default implementation simply copies @var{val} to @var{result} and\n\
+ emits a @code{speculation_barrier} instruction if that is defined.  If\n\
+ @code{speculation_barrier} is not defined for the target a warning will\n\
+ be generated.",

There are some examples later in the thread that shows how this can be used, such as the following. This is written with the assumption that mem[0] is a safe, if potentially incorrect, value to return:

void *mem;

void* f(unsigned untrusted)
{
  if (untrusted < 100)
    return mem[__builtin_speculation_safe_value (untrusted)];
  return NULL;
}


to post comments

Spectre V1 defense in GCC

Posted Jul 11, 2018 0:21 UTC (Wed) by roc (subscriber, #30627) [Link] (6 responses)

The assumption that element[0] is a safe value is fragile and I'm sure it's going to burn people sooner or later. It happens to work in the kernel most of the time because its empty arrays typically have NULL base addresses but in lots of code, that's not the case.

Spectre V1 defense in GCC

Posted Jul 11, 2018 0:41 UTC (Wed) by lambda (subscriber, #40735) [Link] (2 responses)

There is another example which uses return *__builtin_speculation_safe_value (mem + untrusted); so that it will speculate a NULL pointer dereference instead, if you can't afford to leak mem[0], or are in code where you can't tell if leaking mem[0] is safe.

Point taken that the first example isn't necessarily good if you, say, have a zero-length slice that you're bounds checking against, or something of the sort.

Luckily, this intrinsic should only need to be used in relatively few places, which can be intensively code reviewed; anything which allows code to execute within a process but shouldn't have access to all of the data in the process, such as a JavaScript or wasm engine. I feel like it shouldn't be too hard to encapsulate most such bounds checks into a relatively small number of functions, which could be thoroughly checked.

Spectre V1 defense in GCC

Posted Jul 11, 2018 13:13 UTC (Wed) by matthias (subscriber, #94967) [Link] (1 responses)

> Luckily, this intrinsic should only need to be used in relatively few places, which can be intensively code reviewed; anything which allows code to execute within a process but shouldn't have access to all of the data in the process, such as a JavaScript or wasm engine.

I am less optimistic. Actually this affects all parts of code that deal with user input, not only scripting languages. A JPEG image needs to be interpreted to be printed on screen. If I tamper with the image, I might trigger speculative execution in the JPEG library. Certainly harder to exploit than using JavaScript, but is it impossible?

Spectre V1 defense in GCC

Posted Jul 11, 2018 14:16 UTC (Wed) by epa (subscriber, #39769) [Link]

I think it could only be exploited if there were other code running which could sniff for the effects of speculation (changes in the cache state). That code could be in another userland process if it has addresses which happen to share the same cache lines -- I think?

Spectre V1 defense in GCC

Posted Jul 12, 2018 10:06 UTC (Thu) by edeloget (subscriber, #88392) [Link] (2 responses)

> The assumption that element[0] is a safe value is fragile and I'm sure it's going to burn people sooner or later.

It's more or less required by the C standard unless I'm mistaken (for element[n] to be a valid expression, element shall be a valid pointer). gcc developers are not that interested in non-standard code :)

Spectre V1 defense in GCC

Posted Jul 12, 2018 11:17 UTC (Thu) by excors (subscriber, #95769) [Link]

You could write code like in lambda's comment where 'mem' is guaranteed to be a valid pointer if untrusted < 100, but otherwise is invalid. C is perfectly happy with that, because there is no possible code path where you're either calculating or dereferencing the pointer &mem[n] when mem is invalid. But the CPU doesn't care about your idea of code paths, it'll (speculatively) execute whatever arbitrary instructions it feels like, so it might execute instructions to read mem[n] when mem or n or both are invalid.

(As a more realistic example, you could have some kind of dynamic array which stores a size and a pointer to the storage, but that pointer is an uninitialised value if size is 0. Speculatively reading from the 'safe' index 0 could be bad, and the CPU might do that even if the C code always checks size before accessing the storage.)

Spectre V1 defense in GCC

Posted Jul 14, 2018 4:50 UTC (Sat) by roc (subscriber, #30627) [Link]

The C standard only requires element[n] to be a valid expression if it's evaluated during regular (non-speculative) program execution.

It's completely plausible that element[0] is uninitialized and the program never touches it during non-speculative execution, therefore is completely OK w.r.t. the C standard, but the CPU reads element[0] speculatively, leaking information about the uninitialized data.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds