English
推测¶
本文档介绍了推测的潜在影响,以及如何使用通用 API 以可移植的方式缓解不良影响。
为了提高性能并最小化平均延迟,许多现代 CPU 采用了分支预测等推测执行技术,执行可能在稍后阶段被丢弃的工作。
通常,无法从架构状态(如寄存器的内容)观察到推测执行。然而,在某些情况下,可以观察到它对微架构状态的影响,例如缓存中数据的存在或缺失。这种状态可能形成侧信道,通过观察这些侧信道可以提取秘密信息。
例如,在存在分支预测的情况下,边界检查可能会被推测执行的代码忽略。请考虑以下代码
int load_array(int *array, unsigned int index)
{
if (index >= MAX_ARRAY_ELEMS)
return 0;
else
return array[index];
}
在 arm64 上,它可能会被编译为如下汇编序列
CMP <index>, #MAX_ARRAY_ELEMS
B.LT less
MOV <returnval>, #0
RET
less:
LDR <returnval>, [<array>, <index>]
RET
CPU 可能会错误预测条件分支,并推测性地加载 array[index],即使 index >= MAX_ARRAY_ELEMS。该值随后会被丢弃,但推测加载可能会影响随后可被测量的微架构状态。
涉及多个相关内存访问的更复杂序列可能导致敏感信息泄露。基于之前的示例,请考虑以下代码
int load_dependent_arrays(int *arr1, int *arr2, int index)
{
int val1, val2,
val1 = load_array(arr1, index);
val2 = load_array(arr2, val1);
return val2;
}
在推测下,对 load_array() 的第一次调用可能会返回越界地址的值,而第二次调用将影响依赖于该值的微架构状态。这可能会提供一种任意读取原语。
缓解推测侧信道¶
内核提供了一个通用 API,以确保即使在推测下也能遵守边界检查。受基于推测的侧信道影响的架构应实现这些原语。
<linux/nospec.h> 中的 array_index_nospec() 辅助函数可用于防止信息通过侧信道泄露。
调用 array_index_nospec(index, size) 会返回一个清洗后的索引值,即使在 CPU 推测条件下,该值也限定在 [0, size) 范围内。
这可用于保护之前的 load_array() 示例
int load_array(int *array, unsigned int index)
{
if (index >= MAX_ARRAY_ELEMS)
return 0;
else {
index = array_index_nospec(index, MAX_ARRAY_ELEMS);
return array[index];
}
}