4 ms·
I wonder why they aren't using the approach others are taking. That is, compile an array access a[i] into an array of size N as a[i&M] where M is one less than
by dzdt 9y ago
I wonder why they aren't using the approach others are taking.
That is, compile an array access a[i] into an array of size N as a[i&M] where M is one less than the next power of two larger than N.
The logical and is very fast; it can be applied everywhere without a big performance hit. And this removes almost all the attack surface, as the real vulnerability is when the attacker forces speculative access to go way outside the array to unrelated memory that probably even belongs to another process.
Edit: actually there is an even better masking approach the linux kernel is using -- see https://lwn.net/Articles/744287/ https://lwn.net/Articles/744287/
- IshKebab 9y agoYou can't read other process's memory with Spectre. And your solution would only work for statically sized arrays. In that case though it seems like a reasonable thing to do. Not a full solution though.
- dzdt 9y agoSpectre can read from other processes! Meltdown can even read across privilege levels (userland to kernel). But within userland, spectre lets you read anything. See: https://isc.sans.edu/forums/diary/Meltdown+and+Spectre+clearing+up+the+confusion/23197/ https://isc.sans.edu/forums/diary/Meltdown+and+Spectre+clear...
- bennofs 9y agoYou can only read memory that is mapped in the address space of vulnerable processes with Spectre. Usually, that is only memory from the current process and maybe a small bit of shared memory (and code segments of shared libraries, but those don't contain any interesting secrets). So while you can read memory from other processes, those processes need to be vulnerable and you cannot read memory from processes that applied mitigation.
- jjnoakes 9y agoIs this true? I'm no spectre expert but I thought that if you could detect cache changes correctly, you could infer the value of memory that you did not have mapped (but the other process did) as long as the other process changes its cache-touching behavior based on the value of the memory it had access to.
- kllrnohj 9y agoNo, that'd be like claiming buffer overflow attacks let you exploit any other process on the system. It's technically true-ish but misleading. You can only read from a process that you have managed to compromise, and spectre is a new category of exploit vectors not an exploit in and of itself. So you need a process that takes external inputs (you need some way to influence it), and then you need to figure out how that input can resolve to a viable exploit via spectre. If your input validation code is spectre-free (LFENCE, whatever) then you're probably good to go. You don't need everything in your app to be spectre free. In the blog's example, for instance, he just assumes that somehow an attacker can influence the input X. But if there's no way for an attacker to do that because X never comes from any externally-influenced thing then you didn't need any LFENCEs on it. Ensuring that all your external-touching code is completely spectre-free is non-trivial, of course, but it's not like you can just arbitrarily exploit anything you want, and you don't need every array access to be LFENCE'd or mask'd.
- jjnoakes 9y agoIs this true though? I'm no spectre expert, but my layman understanding is that since you can poison branch predictors, you can direct the other process to execute a gadget that wouldn't normally be executed. This means you can conceivably direct the other process to perform speculative operations on inputs you control, even if those code paths would never touch those inputs during normal execution. I suppose if you mitigate the branch predictor poisoning (retpoline perhaps?) then this is not a concern any more.
- kllrnohj 9y agoYou can only influence which branch it takes, you can't force it to jump to an arbitrary place. And there's a limit to how far down that branch it will go, of course. But consider the example function in the article: void victim_function_v01(size_t x) { if (x < array1_size) { temp &= array2[array1[x] * 512]; } } If you can't control x as the attacker you can't really get this to do anything useful no matter which way you manage to get the if to predict. Simply forcing the if to speculate one way or the other does not result in arbitrary memory reads. You need to force the if and control x.
- danieldk 9y agoThat is, compile an array access a[i] into an array of size N as a[i&M] where M is one less than the next power of two larger than N. Doesn't that still permit speculatively getting an out-of-bound element, where the problem gets worse with larger arrays? The following LWN article describes a more sophisticated masking approach by Alexei Starovoitov: https://lwn.net/Articles/744287/ https://lwn.net/Articles/744287/ Of course, the same question still applies: why not use this cheaper masking approach. Are there known problems with it?
- amelius 9y agoI don't think the compiler always knows N. This would then be something the programmer (and/or a library implementer) should provide.