3

This is code snippet of Meltdown assembly language code:

 1. ; rcx = kernel address, rbx = probe array
 2. xor rax, rax
 3. retry:
 4. mov al, byte [rcx]
 5. shl rax, 0xc
 6. jz retry
 7. mov rbx, qword [rbx + rax]

Meltdown uses an array of pages (256*4096 size) where every element is addressed by a 1-byte value to determine what byte [rcx] is. Its paper says it is iterating through all the array's pages to access the first cahe line of every of them and see how much time was taken to do it. If it's done relatively fast, the cache line number is byte [rcx].

Why should Meltdown use the probe array at all to deduce the value of that byte? As I see, when the 4th line's uOP which is responsible for loading byte [rcx] is executed, some page is accessed, and there is the new cache line loaded with the byte's value, unless it already was before this instruction. Why can't Meltdown access that page? Is it because Flush+Reload attack works only for usermode pages?

M. Kalter
  • 35
  • 4

2 Answers2

0

Cache timing attacks don't tell you anything about the value stored in the cached page. They only tell you which page was cached. The trick of Meltdown (and Spectre) is loading one of many pages into the cache depending on a value that the program is not allowed to know, and that the CPU "forgets" very quickly.

Meltdown exploits the fact that the CPU will briefly act as though an illegal memory read succeeded, and use this illicitly-read value (multiplied by the page size) as an index into an array to cause a page of memory (one segment of the probe array, which can be legitimately accessed) to be cached. The exploit then recovers from the exception (segfault / access violation) of the illegal read, and checks how long it takes to access each page of the probe array to figure out which page of the probe array was accessed, which in turn reveals what the "unreadable" value was.


Line 4 accesses some kernel memory and attempts to read a byte from it; this access triggers an interrupt. (The page that contains this kernel memory will end up in the cache, but this fact is irrelevant to us; we already know what address in the kernel we are accessing.) At this point, we start a race to execute the next few lines before the interrupt diverts this entire thread to its interrupt service routine.

Line 5 is simply multiplying the retrieved value by 4096, which is typically the size of a page in memory. This operation is performed entirely on a register and an immediate value, and is very fast, so it will probably happen before the interrupt catches us.

I don't know why line 6 (or line 3) is needed; perhaps the scenario this code is pulled from cares a lot about whether the retrieved value is zero or not.

Line 7 uses the illicitly-obtained value from line 4 (multiplied by 4096 in line 5) as an index into the probe array, causing one of the 256 pages the array spans to be loaded into cache. This instruction will take a while, and therefore probably not complete before the interrupt catches us, but it will start quickly (just add two registers and hand the resulting address off to the memory manager). When the memory manager returns the value at that address, the CPU will throw it away instead of storing it into the register RBX (because the interrupt causes everything done after it was triggered to get thrown away), but that one page of the probe array will stay in the cache anyhow; the CPU isn't "smart" enough to un-cache pages which were only cached based on the illicit read.

Similarly, when the interrupt catches up, it will revert the illicitly-read value from EAX/AL to its pre-line-4 value (which is zero, as set by line 2). In the theoretical model of the CPU, that register never even received the value; the next few lines simply execute as though it had. In practice, the CPU pipeline has many copies of each register, and the copies downstream of the illegal read store the illicit value (which is then used by the downstream instructions), but that illicit value never gets written to the "real" register. Therefore, the attacker cannot do anything much with the illicit value - it only gets a few clock ticks before the interrupt throws out all the downstream instructions, which isn't nearly enough to do something like "write this value to disk" - but initiating a memory read is fast enough and it doesn't have to complete in time.

At this point, the program receives the "OS trap" triggered by the interrupt, and handles it using a handler that it had already set up (signal handler for SEGV on *nix, or SEH exception handler for access violation on Windows, see https://stackoverflow.com/questions/457577/catching-access-violation-exceptions). The program ignores the trap and continues execution (normally this is dangerous, as it implies something unsafe has happened with your pointers, be in this case we are the unsafe thing). The program then times how long it takes to access each page of the probe array, finds the one that can be accessed super-fast (because it was already cached), and divides that index by the page size (4096 again) to get back the byte which was illicitly read.

CBHacking
  • 40,303
  • 3
  • 74
  • 98
  • Well, flush+reload can only tell you how much time it takes to access a page. Even if I can deduce what page is for the kernel address, the only thing I can know about it is its access time. In order to learn what the value of the address is, I have to do some other things like memory access, which Meltdown does. It seems I've answered my question myself – M. Kalter Feb 07 '19 at 22:50
0

his operation is performed entirely on a register and an immediate value, and is very fast, so it will probably happen before the interrupt catches us.