Hacker News new | ask | show | jobs
by samsonradu 3083 days ago
High-level programmer here. Can someone explain please (already read the ELI5 in previous threads) how does the attacker extract the actual data from the processor L1 cache after tricking the branch prediction and have the CPU read from an unauthorized memory location?

I understood the "secret" data stays in the caches for a very short time until the branch prediction is rolled back, which makes this a timing attack but don't get how you actually read it.

EDIT

So perhaps someone can ELI5 me "4.2 Building a Covert Channel" [1] from the Meltdown paper which is what I didn't understand.

[1] https://meltdownattack.com/meltdown.pdf

3 comments

Mostly high-level programmer. I may be wrong or be thinking of another recent attack but my understand was this: the attacker allocates 256 seperate pages, ensures they're not in memory and then runs code like this:

    if(false_but_predictive_execution_cant_tell)
    {
      int i = (int)*protected_kernel_memory_byte;
      load_page(i, my_pages);
    }
Then it becomes a matter of checking speed of reading from those pages. Which ever one is too fast to be loaded when read must be the value read from protected memory.
Caveat: I am also a high level programmer.

My understanding is that the problem is that the data in the cache _isn't_ rolled back.

You fetch the secret data. You then fetch a different memory addressed based on the contents of the secret data e.g. fetch((secret_bit * 128) + offset) [1] so if secret_bit is 0 it's fetched the memory at offset into the cache, if secret_bit is 1 it's fetched the memory at offset+128 into the cache.

After the speculative work is rolled back, the data that it fetched into the cache still remains. You then time how long it takes to fetch offset and offset+128. If offset comes back quickly, secret_bit was 0. If offset+128 comes back quickly, secret_bit was 1.

_That_ is where the timing attack part comes in: "timing attack" refers to using measurements of how long something took to glean information, not that you need to do it quickly.

[1] In reality you do it on the byte level and use &, but I wanted to keep it to guessing a single bit to make it simpler.

> You fetch the secret data. You then fetch a different memory addressed based on the contents of the secret data ...

I was under the impression that there is no interface to read data from the CPU caches and that the cache is managed by the CPU itself only.

Right, which makes it a bit of a tricky attack to pull off. But if you know what you're doing you can do some operation that requires memory address x and be reasonably sure it will end up in the CPU cache. If you then do an operation on memory address x, and it happens really quickly, and you do an operation on memory address x+128, and it happens a bit slower, you can assume that x was in the cache and x+128 wasn't.
Yes, I got the part where you can time if memory address X is in cache and X+128 isn't. But how does one read the data at memory address X?
You load it into a register. If you're trying to drive it from a high level language, I guess you can do something like an add which will get compiled into instructions to load it into a register first.
It does not "stays in the caches for a very short time until the branch prediction is rolled back", the core of the problem is that speculative instructions caused by out of order execution or branch prediction leave a side-effect (whether some memory location was fetched to cache or not) that can be read for a long time afterwards.

The covert channel consists of a "sender" and a "receiver". The receiver can't extract contents of L1 cache, but it can detect which pages were in cache by timing differences. So the sender encodes the secret data by fetching particular addresses calculated so that the receiver can afterwards recover the secret by verifying which page(s) were in cache.

In Meltdown attack, the sender consists of instructions controlled by you - e.g. x=memory_you_shouldn't_access; y=array[1000x] - and after an Intel processor notices that you shouldn't access that memory and rolls back the instructions (invalidating y and x), the 1000x location was already pulled to cache, and you can check - is array[1000] cached? is array[2000] cached? is array[142000] cached? to determine x.

In Spectre attack, the sender consists of code in the vulnerable application that happens to contain similar instructions. Spectre attack means that if your application anywhere contains code like if (parameter is in bounds) {x=array1[parameter]} (...possibly some other code...) y=array2[x], then any attacker that (a) runs on the same CPU and (b) can manipulate the parameter somehow can trick this code to process the path "protected" by 'if' and reveal random memory out of bounds of that array1. The difference from ordinary buffer overflow bugs is that code like that is normal, common and (in general) not a bug, since the instructions "don't get executed", and the vulnerability persists even if you validate all input.