Search code examples
c++securityspectre

Why does the index get multiplied by 512 in the Spectre research paper?


I'm currently trying to understand demo-code that is included in the Spectre research paper, and I don't really understand why array1[x] is multiplied by 512.


void victim_function(size_t x) {
    if (x < array1_size) {
        temp &= array2[array1[x] * 512];
    }
}


Solution

  • TL;DR: to prevent entries being stored on the same cache line, which would break the Flush+Reload side channel.


    With "the Spectre research paper", I'm assuming you're implying the paper "Spectre Attacks: Exploiting Speculative Execution" by P. Kocher et al.

    array2 is the flush+reload side channel. This means that it is used to leak the value of array1[x]. Note that array2 is defined as uint8_t array2[256 * 512];. The 256 resembles the entries of the array, while the 512 resembles an offset between them.

    The reason for this offset has to do with some background information of flush+reload. Basically, flush+reload is an array that is flushed (read: removed) from cache memory after initialization. Then, the attacker waits for a while. If the victim loads an index from the flush+reload array, then it will be stored in cache memory (and stay there for a while). Later, the attacker loads every index of the flush+reload array, and times how long this takes. If it takes long (say, >=200 cpu cycles), then the index is in normal memory. If an index loads fast (say, <200 cpu cycles), then the index was in cache, meaning the attacker can conclude the victim used this index.

    The flush+reload implementation used is an 8-bit one, meaning it has (2^8) 256 entries. A (byte)value of array1 is used as an index for the flush+reload array. Imagine the value of array1[array1_size] is 'a' (97), using this value as an index in array1 would load the 97th index into cache. When the attacker finds out the 97th index is in cache, they know the out-of-bounds value was 97.

    However, there's the weird 512 value. When something is flushed from cache, this is done with the assembly operation clflush. However, this operation flushes an entire cache line (usually 64 bytes). As a result, the flush+reload array cannot simply be an array of 256 bytes. Because while the attacker loads the first entry to see whether it is in cache, the entire cache line gets loaded into cache. This is why an offset is used, to prevent multiple entries residing on the same cache line.

    Now, why did they use 512 instead of 64? I can't say for sure, but based on my own experience it sometimes helps to increase the value to something higher than the minimum of 64.

    A more elaborate explanation of flush+reload and Spectre V1 can be found in a GitHub repository of mine. It also includes code examples that might be more readable for beginners.