diff options
Diffstat (limited to 'chap/chap3.tex')
-rw-r--r-- | chap/chap3.tex | 376 |
1 files changed, 238 insertions, 138 deletions
diff --git a/chap/chap3.tex b/chap/chap3.tex index ec0f824..ffbdbe1 100644 --- a/chap/chap3.tex +++ b/chap/chap3.tex @@ -10,7 +10,7 @@ Meltdown 和 Spectre 及其多种变体被发现后,研究者提出了多种 \section{Meltdown型攻击的防御} -Meltdown型攻击利用了瞬时指令可以读取体系结构层次上不可访问的数据,并且 +Meltdown型攻击利用了暂态指令可以读取体系结构层次上不可访问的数据,并且 用此数据做计算。因此一种防御方式是使体系结构层次上不可访问的数据,在微 架构层次上仍然不可访问。 @@ -24,7 +24,7 @@ Meltdown 攻击的方案。它的作用是在用户空间中去除内核空间 \section{Spectre型攻击的防御} -Spectre 型攻击的防御方案可以分为三类:阻止推测式执行、防止瞬时指令访问 +Spectre 型攻击的防御方案可以分为三类:阻止推测式执行、防止暂态指令访问 秘密数据、切断隐蔽信道或降低隐蔽信道的精度。研究者在软件和硬件方面都提 出了防御方案。 @@ -35,34 +35,75 @@ Intel 和 AMD 都提出了在分支指令后插入 lfence 指令阻止推测式 为一条串行化指令使用,可以在 lfence 指令提交前阻止新的指令执行,从而阻 止了程序在推测式执行中对秘密数据进行操作。 -\Todo: 增加 SLH,retpoline,index masking 相关的代码 - 由于 lfence 性能开销大,LLVM提出推测式装载指令加固(Speculative -Load Hardening)\supercite{spec-load-hardening} 技术,它的作用是在指令 -流中添加数据相关,使得装载指令使用的地址依赖于分支结果。 +Load Hardening)\supercite{spec-load-hardening} 技术,它的做法是将可能 +为 Spectre 组件的代码转为图\ref{fig:SLH}中的代码: + +\begin{figure}[htbp] +\begin{minted}[frame=single]{C} + if (condition) { + // ?: 的实现可以用无分支的 cmov + predicate_state = !condition ? 0 : predicate_state; + // ... a lot of code ... + // 加固指针,使得它不可用于访问 + pointer1 &= predicate_state; + leak(*pointer1); + } else { + predicate_state = condition ? 0 : predicate_state; + // ... more code ... + // 或加固装载得到的数据,使得数据无法泄露 + int value2 = *pointer2 & predicate_state; + leak(value2); + } +\end{minted} +\caption{推测式装载指令加固产生的代码} +\label{fig:SLH} +\end{figure} + +产生的代码在两个分支中,都计算一个分支条件的状态,并将其作为存储访问指 +令或泄露数据指令的一个数据依赖,从而在得出分支结果之前,无法访问或泄露 +一个秘密数据。 retpoline\supercite{retpoline} 是 Google 提出的防御 Spectre-BTB 的方法。 它的作用是把程序中的间接转移指令修改为一个指令序列,最终使用 ret 指令 完成跳转,从而使用 RSB 而不是 BTB 来进行间接转移的转移预测。 +% TODO:增加 retpoline 的代码? + % index masking Webkit 在数组访问中使用 index masking\supercite{webkit} 方法,它让数组 -下标和一个值进行与操作,去掉下标高位的1,将数组下标控制在一定范围内, -防止处理器在推测式执行中访问数组指定范围之外的数据。Linux 构造了一个粒 -度更细的 array\_index\_nospec 宏\supercite{linux-spec},使得在推测式执 -行的过程中,数组的索引始终在界内,避免了推测式的访问秘密数据。 +下标和一个值进行与操作,去掉下标高位的1,将数组下标控制在一定范围内,防 +止处理器在推测式执行中访问数组指定范围之外的数据。Linux 构造了一个粒度 +更细的 array\_index\_nospec\supercite{linux-spec},它的功能如 +图\ref{fig:array-index-nospec}: + +\begin{figure}[htbp] +\begin{minted}[frame=single]{C} + size_t array_index_nospec(size_t index, size_t size) { + mask = ~(signed long)(index | (size - 1 - index)) >> 63; + return (index & mask); + } +\end{minted} +\caption{array\_index\_nospec} +\label{fig:array-index-nospec} +\end{figure} + +如果数组索引 index 小于 size,则 mask 为一个所有位为 1 的值,产生的索 +引不变,否则 mask 为 0,产生的索引为 0,从而推测式执行时的数组索引始终 +在数组大小范围内。 % poison value -Webkit 还使用了指针投毒(pointer poisoning)的方式,它考虑分支用于做类 -型检查的情形。对于不同类型的数据,Webkit 将这种类型的指针和一个类型对 -应的值异或,要使用的时候再异或这个值得到指向数据的指针,如果类型不匹配, -则得到的指针会指向一个程序无法访问的内存区域。通过这种方法,程序在推测 -式执行中无法通过这种指针访问到程序中的数据,从而避免了秘密数据的泄露。 +Webkit 还使用了指针投毒(pointer poisoning)\supercite{webkit}的方式, +它考虑分支用于做类型检查的情形。对于不同类型的数据,Webkit 将这种类型的 +指针和一个类型对应的值异或,要使用的时候再异或这个值得到指向数据的指针, +如果类型不匹配,则得到的指针会指向一个程序无法访问的内存区域。通过这种 +方法,程序在推测式执行中无法通过这种指针访问到程序中的数据,从而避免了 +秘密数据的泄露。 % site isolation, \Fixme: needs citing Google 为 Chrome 浏览器使用了站点隔离(site isolation)技术,使得浏览 器用不同的进程访问不同的网站,从而攻击者无法通过用 Javascript 通过侧信 -道攻击获取另一站点相关的数据。 +道攻击获取另一站点相关的数据。\supercite{here-to-stay} % timer reduction 降低计时器精度可以降低计时攻击所用侧信道的精度,一个例子是在浏览器中降 @@ -153,31 +194,42 @@ SafeSpec\supercite{safespec}提出了一种原则性的方法来保护处理器 \subsubsection{InvisiSpec} -%%%%%% gtran %%%%%% - -\Todo: InvisiSpec introduction, 去掉最前面的背景介绍部分 - -\Fixme: 重新翻译 - -在本文中,我们提出了InvisiSpec,这是一种通过在数据缓存层次结构中使推测不可见来抵御多处理器中的硬件推测攻击的新策略。目标是通过多处理器数据高速缓存层次结构来阻止微架构隐蔽和侧通道,这是由于推测性负载 - 例如,源自高速缓存集占用,线路替换信息和高速缓存一致性状态的信道。我们希望不仅防止基于分支机构推测的类似幽灵的攻击;我们还希望防止任何投机负载可能构成威胁的未来攻击。 - -InvisiSpec的微架构基于两种机制。首先,不安全的推测性加载将数据读入新的推测缓冲区(SB)而不是缓存中,而不修改缓存层次结构。 SB中的数据不会观察缓存一致性事务,这可能导致丢失内存一致性违规。我们的第二种机制解决了这个问当推测性负载最终是安全的时,InvisiSpec硬件通过将其重新发送到内存系统并将数据加载到高速缓存中使其对系统的其余部分可见。在此过程中,如果InvisiSpec认为负载可能违反了内存一致性,InvisiSpec会验证负载首次读取的数据是否正确 - 如果数据不正确则压缩负载。 - -% memory consistency - -\Todo: InvisiSpec 考虑了内存模型,下面是关于 TSO 和 RC 的介绍 - -\Fixme: 重新翻译 - -内存一致性模型(或内存模型)指定内核执行内存操作的顺序,并由共享内存系统中的其他内核观察[10]。当商店退休时,其数据将存入写缓冲区。从那里,当内存一致性模型允许时,数据被合并到缓存中。我们说当数据合并到缓存中时执行存储,并且所有其他核心都可以观察到存储。负载可以在到达ROB头之前从内存中读取。我们说负载是在接收数据时执行的。负载可以从存储器读取并且不按顺序执行 - 即,在ROB中的早期加载和存储之前。无序负载可能导致内存一致性违规,核心通过推测执行的压缩和回滚机制恢复[11]。细节取决于内存模型;我们将在下面描述两种常见模型,我们将其假设为添加InvisiSpec的基线。 - -总存储顺序(TSO)[12],[13]是x86架构的内存模型。 TSO禁止所有可观察的加载和存储重新排序,但存储→加载重新排序,即当负载绕过较早的存储到不同的地址时。实现通过确保负载在执行时读取的值在负载退出时保持有效,从而阻止可观察负载→负载重新排序。如果核心接收到负载读取的线路的高速缓存无效请求(或遭受高速缓存驱逐),则通过压缩已执行但尚未退出的负载来维持此保证。通过使用FIFO写入缓冲区来防止存储→存储重新排序,确保存储按程序顺序执行。如果需要,可以通过使用fence指令分隔存储和加载来防止存储→加载重新排序,该指令在完成所有先前的访问之前不会完成。原子指令具有栅栏语义。 - -释放一致性(RC)[14]允许任何重新排序,除了跨同步指令。装载和存储不得通过先前的获取或随后的发布进行重新排序。因此,仅当存在先前的非退休获取并且具有非FIFO写缓冲区时,RC实现压缩在接收到其高速缓存行的无效时执行加载。 +InvisiSpec \supercite{invisispec} 使推测式执行产生的副作用在数据缓存中 +不可见,从而可以防御利用推测式执行的攻击。它的目标是阻止由推测式执行的 +装载操作产生的微架构侧信道和隐蔽信道,如缓存占用、缓存行替换算法信息、 +缓存一致性状态等。它不但要防御基于分支推测式执行的 Spectre 攻击,而且 +还要防御未来可能利用到推测式执行中的装载操作的攻击。和 SafeSpec 不同, +InvisiSpec 支持多处理器。 + +InvisiSpec 的微架构基于两种机制。第一个机制中,不安全的推测式执行的装载 +操作将数据读入至推测式执行缓冲区(Speculative Buffer,SB)中,而不读入 +缓存,从而不修改缓存存储层次的状态。由于缓冲区中的数据不产生缓存一致性 +事务,因此可能造成内存一致性违例。第二个机制解决这个问题,当推测式执行 +的装载操作变得安全时,处理器将这个操作重新发射至存储系统,使其对整个系 +统可见。如果这个装载指令可能造成访存违例,InvisiSpec 验证读出的指令是 +否正确,如果验证失败,则重新执行这个访存指令及其后续指令。 +% squash the load + +InvisiSpec 在不同的内存一致性模型下,会使用不同的执行策略。内存一致性模 +型(或内存模型)指定在一个共享内存的系统中,处理器核执行访存操作,并使 +其对其他处理器核可见的顺序。在存储指令执行结束后,存储的数据存放在写缓 +冲区中,随后写入缓存。当写操作写回缓存后,其他核可以看见这个数据,存储 +操作在此时完成。而装载操作在提交前从存储系统读取数据,当它得到数据时, +则称为装载操作完成。装载操作的完成可以在程序序列中其他的访存操作之前, +可以导致内存一致性违例,此时处理器需要用恢复机制重新执行这个装载指令及 +其后的指令。 + +TSO(Total Store Order)是 x86 体系结构所用的内存模型。TSO 允许一个装载 +操作在不同地址的存储操作之前完成,除此之外禁止所有可见的装载和存储的重 +排序。在实现中,为了防止可见的装载指令的重排序,需要确保一个装载操作读 +入的数据,在提交时仍然有效。如果处理器核遇到一个外部的缓存失效请求,则 +需要重新执行这个装载操作。存储指令的顺序由先入先出的写缓冲区保证。 + +RC(Release Consistency)则允许任何访存操作的重排序,除非使用同步指令。 \begin{tabular}{|c|c|} \hline -攻击 & 瞬时指令的来源\tabularnewline +攻击 & 暂态指令的来源\tabularnewline \hline \hline Meltdown & \multirow{2}{*}{虚拟内存异常}\tabularnewline @@ -196,17 +248,23 @@ Speculative Store Bypass & 装载指令和更早的存储指令地址别名\tabu \hline \end{tabular} +InvisiSpec 关注不安全的推测式执行的装载指令(Unsafe Speculative +Load,USL),在 Spectre 的攻击模型中,USL 是在未决分支之后的装载指令, +在得到分支结果后,正确路径上的 USL 转为安全的装载指令。而在未来攻击的模 +型中,USL 变为安全仅当指令到达 ROB 头部,或不被可能中断该指令的事件回 +卷。 + \Todo: InvisiSpec V: INVISISPEC: THWARTING SPECULATION ATTACKS \Fixme: 重新翻译以下内容 -1)不安全的推测负载:严格地说,任何在到达ROB头部之前启动读取的负载都是推测负载。在这项工作中,我们对可能由于推测而产生安全漏洞的(大)投机负载子集感兴趣。我们称这些负载为Unsafe Speculative Loads(USLs)。作为USL的一组推测性负载取决于攻击模型。 +%1)不安全的推测负载:严格地说,任何在到达ROB头部之前启动读取的负载都是推测负载。在这项工作中,我们对可能由于推测而产生安全漏洞的(大)投机负载子集感兴趣。我们称这些负载为Unsafe Speculative Loads(USLs)。作为USL的一组推测性负载取决于攻击模型。 -在Spectre攻击模型中,USL是遵循未解决的控制流指令的推测性负载。一旦控制流指令解决,跟随它的USL在分支的正确路径中转换到安全负载 - 尽管它们仍然是推测性的。 +%在Spectre攻击模型中,USL是遵循未解决的控制流指令的推测性负载。一旦控制流指令解决,跟随它的USL在分支的正确路径中转换到安全负载 - 尽管它们仍然是推测性的。 -在我们的未来攻击模型中,USL是所有可以被先前指令压扁的推测性负载。一旦USL变为(i)非推测性的,因为它到达ROB的头部或者(ii)推测不能通过任何先前的指令(或推测性的不可压缩的短路)变为安全负载。推测性非可压缩负载是(i)不在ROB的头部并且(ii)在ROB之前仅通过指令而不是由表I中的任何压扁事件压缩的未来的负载。请注意,在表中,其中一个压缩事件是中断。因此,使负载安全包括延迟中断直到负载到达ROB的头部。 +%在我们的未来攻击模型中,USL是所有可以被先前指令压扁的推测性负载。一旦USL变为(i)非推测性的,因为它到达ROB的头部或者(ii)推测不能通过任何先前的指令(或推测性的不可压缩的短路)变为安全负载。推测性非可压缩负载是(i)不在ROB的头部并且(ii)在ROB之前仅通过指令而不是由表I中的任何压扁事件压缩的未来的负载。请注意,在表中,其中一个压缩事件是中断。因此,使负载安全包括延迟中断直到负载到达ROB的头部。 -要了解为什么这两个条件允许USL过渡到安全负载,请考虑以下内容。 ROB头上的负载本身不能是瞬态的;它可以被压扁(例如,由于异常),但它是一个正确的指令。关于推测性非可压缩负载也可以这样说。这是因为,虽然不在ROB头部,但是它们不能被任何先前的指令压扁,因此可以被认为是ROB头部处的指令的逻辑扩展。 +%要了解为什么这两个条件允许USL过渡到安全负载,请考虑以下内容。 ROB头上的负载本身不能是瞬态的;它可以被压扁(例如,由于异常),但它是一个正确的指令。关于推测性非可压缩负载也可以这样说。这是因为,虽然不在ROB头部,但是它们不能被任何先前的指令压扁,因此可以被认为是ROB头部处的指令的逻辑扩展。 2)使USLs看不见:InvisiSpec背后的想法是让USL隐形。这意味着USL无法以任何其他线程可见的方式修改缓存层次结构,包括一致性状态。 USL将数据加载到我们称为Speculative Buffer(SB)的特殊缓冲区中,而不是加载到本地缓存中。如上所述,USL可以转换到安全负载的时间点。此时,称为可见性点,InvisiSpec采取的行动将使USL可见 - 即,将使USL在内存层次结构中的所有副作用对所有其他线程都明显。 InvisiSpec通过重新加载数据使USL可见,这次将数据存储在本地缓存中并更改缓存层次结构状态。负载可能仍然是推测性的。 @@ -407,12 +465,12 @@ CSF 防御 Spectre 的开销在 8\% 以下。 \subsubsection{Conditional Speculation} -条件推测式执行(Conditional Speculation) -\supercite{conditional-speculation} 是一种检测推测式执行中的潜在的泄露 -数据的访存指令,并阻止其执行的方法。为了检测可能在推测式执行中泄露数据 -的指令,此工作提出安全依赖(security dependence)的概念,类似于数据依 -赖和控制依赖,这种新的依赖性用于描述泄漏微架构信息具有潜在安全风险的推 -测性执行的指令。对于信道 $c$,指令 $j$ 安全依赖于指令 $i$,如果: +Conditional Speculation \supercite{conditional-speculation} 是一种检测 +推测式执行中的潜在的泄露数据的访存指令,并阻止其执行的方法。为了检测可 +能在推测式执行中泄露数据的指令,此工作提出安全依赖(security +dependence)的概念,类似于数据依赖和控制依赖,这种新的依赖性用于描述泄 +漏微架构信息具有潜在安全风险的推测性执行的指令。对于信道 $c$,指令 $j$ +安全依赖于指令 $i$,如果: \begin{itemize} \item 在程序序列中,指令 $j$ 在 $i$ 之前 @@ -426,6 +484,7 @@ CSF 防御 Spectre 的开销在 8\% 以下。 根据以上定义,表\ref{tab:secdep}总结了 Spectre 攻击中的主要的安全依赖。 Spectre 攻击的安全依赖性来自两种情况,访存-访存推测和分支-访存推测。 +\begin{table} \begin{tabular}{|c|c|c|} \hline Spectre 变体 & 指令 $i$ & 指令 $j$\tabularnewline @@ -440,6 +499,9 @@ Spectre v4 & 访存 & 访存\tabularnewline SpectrePrime & 条件分支 & 访存\tabularnewline \hline \end{tabular} +\caption{Spectre 攻击中的安全依赖} +\label{tab:secdep} +\end{table} 在条件推测式执行的流水线中,在发射队列中引入了安全冒险检测,以识别具有 安全依赖的可疑的不安全指令。只要在执行阶段确认了真正的冒险,那些不安全 @@ -447,113 +509,151 @@ SpectrePrime & 条件分支 & 访存\tabularnewline % the following is from gtran -\Todo: V-B Security Hazard Detection in Issue Queue - -\Fixme: 重新翻译 - -我们设计了基于比特矩阵的安全检测逻辑,如图2所示。比特矩阵是一些商品处理器用来跟踪数据依赖和年龄信息的流行方式[26],[27],[28]。通常,数据依赖矩阵和年龄矩阵一起可以确定要发布的指令。对于安全检测模块,安全依赖矩阵还必须确定要发布的指令是否具有任何安全依赖性。 - -矩阵组织:安全依赖矩阵需要有效地支持行和列访问。假设问题队列具有N个项,则安全依赖性矩阵将包含NxN位的寄存器阵列。它由IQPos(问题队列位置)编制索引。此矩阵的读端口数等于调度宽度,写端口数等于发布宽度。给定任何指令X,IQPos X表示其在问题队列中的位置。如果Matrix [IQPos X,IQPos Y]的值为1,则X对Y具有安全依赖性。否则,这意味着它们之间没有安全依赖性。矩阵初始化:当新指令X被分派到问题队列中时,一个条目被分配索引IQPos X.对于此时在问题队列中有效的每个指令Y,计算Matrix [IQPos X,IQPos Y]根据以下公式。 - -Matrix[X, Y ] = (IssueQ[X].opcode == MEMORY) -and (IssueQ[Y ].opcode == MEMORY or BRANCH) -and IssueQ[Y ].valid -and \!IssueQ[Y].issued - -此公式基于以下逻辑来确定指令之间的安全依赖性。首先,如果在X被分派到问题队列之前Y是有效的,则意味着Y在X之前。其次,对于Specter变体,我们仅检查存储器指令是否依赖于先前的分支或存储器指令。第三,如果在发出存储器指令时,先前的分支或存储器指令仍然在问题队列中等待,则该存储器指令将被认为具有安全依赖性。 - -危险检测:图2说明了问题队列的三个阶段选项。在第一阶段,数据依赖矩阵生成依赖向量。在第二阶段,然后将该向量发送到年龄矩阵以选择要发布的最早的就绪指令。在第3阶段,对于选择发布的那些指令,查询安全依赖矩阵以获得它们的安全依赖性,然后更新问题队列的相应条目的状态。特别是,安全依赖矩阵的每一行中的位由OR运算处理,结果表明是否存在潜在的安全隐患。当选择发出一条指令并检测到安全危险时,它将被标记为可疑推测标记。 - -依赖性清除:在发出一条指令X后,更新向量寄存器中的相应位将被设置为0.由IQPos X索引的安全依赖矩阵列将在下一个周期复位。这种操作意味着清除了相应指令和X之间的安全依赖性。 - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -为了性能,安全性和透明性的平衡,条件推测式执行提出了两种过滤机制,以找 -出错误识别的安全安全冒险。基于缓存命中的冒险过滤器针对命中缓存的推测式 -执行指令。因为它们的推测式执行不会改变缓存的内容,所以它们是安全的。 -另一个提出的过滤器,基于可信页面缓冲区(Trusted Page Buffer, TPBuf)的 -冒险过滤器,从另一个角度识别安全的推测推测式执行的指令。对于使用基于共 -享内存(例如 Flush+Reload)的缓存侧通道攻击窃取内存信息的 Spectre 变体, -他们对恶意组件的推测式执行具有名为 S-Pattern 的共同特征。 TPBuf 从所有 -推测式执行中捕获 S-Pattern,对于任何推测式执行的访存指令,如果它与 -S-Pattern 不匹配,则被认为是安全的。 - -% gtran - -\Todo: V-C cache-hit based hazard filter 和 V-D Trusted Pages Buffer -based Hazard Filter - -\Fixme: 重新翻译 +\begin{figure}[htbp] + \centering + \includegraphics[width=0.8\textwidth]{conditional-spec.eps} + \caption{安全冒险的检测\supercite{conditional-speculation}} + \label{fig:cond-spec} +\end{figure} -一种简单的危险消除方法是简单地阻止在发布队列中标记有可疑推测标记的所有指令。这样的政策显然会导致性能下降。同时,只有导致高速缓存未命中的内存请求将改变高速缓存内容,并且大多数应用程序表现出良好的时间和空间局部性。受这两个观察的启发,基于缓存命中的危险过滤器被提议用于减少保守地不执行具有可疑推测标记的指令的性能影响。 +安全冒险的检测逻辑用一个矩阵实现。处理器中通常用矩阵跟踪数据依赖,而在 +安全检测中,处理器使用安全依赖矩阵确定要发射的指令是否存在安全相关。添 +加了安全依赖矩阵的发射阶段如图\ref{fig:cond-spec}所示。 + +安全依赖矩阵是一个 NxN 比特的数组,其中 N 为发射队列大小,用发射队列的 +位置索引。对于指令队列中位置为 X, Y 的指令,如果该矩阵 Matrix 的位 +置 $Matrix[X,Y]$ 为 1,则 X 安全依赖于 Y. + +当新的指令分发至发射队列时,处理器在发射队列的位置 X 处分配一个条目, +此时对发射队列中所有有效的指令 Y,计算 $Matrix[X,Y]$: + +$Matrix[X,Y] = (IQ[X].opcode == Memory) \\ + \phantom{=}\& (IQ[Y].opcode == Memory || IQ[Y].opcode == Branch) \\ + \phantom{=}\& IQ[Y].valid \\ + \phantom{=}\& !IQ[Y].issued$ + +这个公式的含义如下:X 在进入发射队列之前 Y 有效,则意味着 Y 在 X 之前; +对于多种 Spectre 变体,只需要检查访存指令是否依赖于先前的分支或访存指 +令;如果在访存指令发出时,此前的分支或访存仍在等待,则认为新的访存指令 +存在安全相关。 + +在实现中,图\ref{fig:cond-spec}展示了发射队列的三个阶段。第一阶段中,数 +据依赖矩阵生成一个依赖向量;第二阶段中,该向量发送至年龄矩阵,用于选择 +最老的可发射的指令;在第三阶段,处理器将被选择要发射的指令放进安全依赖 +矩阵,查询它们的安全相关性。当一个将要发射的指令存在安全冒险时,它会被 +标上可疑推测式执行(\emph{suspect speculation})标记。 + +在指令 X 发射后,更新向量寄存器中的对应位设为0,在下一周期,依赖矩阵中 +X 对应的一列将会重置,意味着待发射指令和 X 的之间的安全相关被清除。 + +一种简单的冒险消除方式是阻止发射队列中标记了可疑推测式执行指令的执行, +但是这种策略会导致性能下降。为了实现性能,安全性和透明性的平衡,条件推 +测式执行提出了两种过滤机制,以找出错误识别的安全安全冒险。基于缓存命中 +的冒险过滤器针对命中缓存的推测式执行指令。因为它们的推测式执行不会改变 +缓存的内容,所以它们是安全的。另一个提出的过滤器,基于可信页面缓冲区 +(Trusted Page Buffer, TPBuf)的冒险过滤器,从另一个角度识别安全的推测 +推测式执行的指令。对于使用基于共享内存(例如 Flush+Reload)的缓存侧通道 +攻击窃取内存信息的 Spectre 变体,他们对恶意组件的推测式执行具有名 +为 S-Pattern 的共同特征。 TPBuf 从所有推测式执行中捕获 S-Pattern,对于 +任何推测式执行的访存指令,如果它与 S-Pattern 不匹配,则被认为是安全的。 % hit-based -对于标记有可疑推测标志的存储器指令,它将被推测性地发布到存储器访问管线。如果推测性存储器访问命中L1高速缓存,则其执行将继续作为正常存储器指令。但是,如果遇到L1高速缓存中的未命中,则丢弃的请求将被丢弃。信号从L1高速缓存发送回问题队列,重新发出逻辑应该应用于存储器指令,直到其安全依赖性得到解决。这些被阻止的指令在发布队列中等待安全依赖性在重新发布之前清除。此设计仅需要L1高速缓存控制逻辑中的最小更改:将不会处理具有可疑推测标记的未命中请求。 +考虑到大多数程序有良好的时间局部性和空间局部性,一级缓存命中的访存请求 +不会修改缓存的内容,因此可以使用基于缓存命中的冒险过滤器,以减少阻止可 +疑推测式执行的指令造成的性能损失。只要访存指令的访问命中一级缓存,则该 +指令正常执行,但是如果缓存缺失,则该指令将会重新回到发射队列,直到安全 +依赖得到解决,才可疑重新发射。这个过滤器只需要对一级缓存的控制逻辑做少 +量修改,即不处理标记为可疑推测式执行的访存请求。 % TPBuf -基于缓存命中的危险过滤器仅考虑在L1 DCache中命中的推测性内存指令是安全的,并允许它们被推测性地执行。对于具有高L1 DCache未命中率的应用程序,它将无法恢复投机执行的大部分好处。对于这些应用,我们提出了一种新的危险过滤器可信页面缓冲器(TPBuf)来检测L1 DCache中丢失的指令的安全推测。 TPBuf基于以下观察:并非所有推测性缓存未命中都可用于构建推测性侧通道。基于第3节中定义的威胁模型,我们关注使用共享内存(例如,Flush + Reload)范例和非法访问内存页面信息的Spectre变体。如图3所示,恶意小工具的推测性执行在我们的目标Spectre攻击中被用作通用内存访问模式。特别是,观察到恶意推测执行流程总是包含两个特殊的存储器指令(A和B)。这两条指令具有以下用法和行为。 - -1)A用于推测性地访问敏感数据。 B推测访问攻击者共享的内存区域,用于构建受害者和攻击者之间的缓存侧通道。由于用于侧信道的秘密数据和存储区域通常位于不同的存储页面,因此这两条指令访问不同的页面。 - -2)为了构建缓存侧通道,攻击者需要首先刷新特定的共享内存数据。然后,B的诱导推测执行具有高速缓存未命中,因此将高速缓存行重新加载到L1 DCache中。攻击者可以通过缓存侧信道感知状态信息的这种变化。因此,B的高速缓存未命中对于通过高速缓存侧信道泄漏敏感信息是必要的。 - -3)B是数据依赖于A.A的结果用于计算共享存储区域的索引。这种精心设计的依赖性也是攻击者推断秘密价值的另一个重要点。 - -在上述观察的推动下,我们将上述共同特征行为称为S模式。具体地,如果观察到推测执行的指令序列具有以下特征,则我们认为该推测指令序列具有S模式行为。 - -1)至少有两个指令(A和B)分别访问不同的存储页面。 +如果一个程序的一级缓存命中率低,则基于缓存命中的冒险过滤器仍然会阻止大 +量的推测式执行。对于利用共享内存构造侧信道的 Spectre 攻击方式,并非所有 +的一级缓存缺失都可作为隐蔽信道使用。因此可以使用基于可信页面缓冲区的冒 +险过滤器,检测这些指令中安全的指令。在这些攻击中,Spectre 组件的执行中 +包含两个特殊的访存指令,记为 A 和 B,它们有如下的行为: + +\begin{enumerate} +\item A 推测式地访问敏感数据,B 推测式地访问攻击者和受害者共享的内存区 + 域,用于构造受害者和攻击者之间的侧信道。通常秘密数据的内存区域和用于 + 构造侧信道的共享内存区域在不同的内存页,因此它们访问不同的页。 +\item 为了构造缓存侧信道,攻击者需要清除共享内存对应的缓存行,其后处理 + 器推测式执行中访问 B 造成缓存缺失,重新将缓存行载入一级缓存,攻击者 + 再探测共享内存的缓存行发现这一变化。因此要通过缓存侧信道泄露信息,需 + 要 B 在缓存中不命中。 +\item 指令 B 依赖于指令 A,A的结果用于计算共享内存区域的索引。这种设计 + 也是攻击者推断处秘密数据要点。 +\end{enumerate} + +在 TPBuf 过滤器中,以上行为模式称为 S-pattern. 如果观察到推测式执行的 +指令序列具有以下特征,则认为它具有 S-pattern: + +\begin{enumerate} +\item 至少有两个指令 A 和 B 访问不同的内存页 +\item 指令 B 导致一级缓存缺失 +\item B 依赖于 A +\item A 和 B 之间可能有多个指令 +\end{enumerate} + +TPBuf 它记录了所有推测式的存储器访问请求,当一个新的请求在一级缓存中缺 +失时,其页地址将会和 TPBuf 中其他访问的页地址比较,如果有至少一个访问访 +问了不同的页,并且正在写回阶段,则认为新的请求不安全。 -2)指令B导致L1 DCache未命中。 - -3)指令B具有数据依赖于指令A. - -4)A和B之间可能有多个指令(计算,存储器或其他类型的指令)。 - - 尽管Spectre攻击的恶意小工具被称为S-Pattern行为,但应该注意的是,具有S-Pattern的指令流不一定是幽灵攻击。出于安全原因,我们试图在微体系结构层面上防止形成具有S-Pattern的推测性指令流。在确保安全性的同时,这种机制自然也会导致性能损失。在第6节中,我们详细评估了该策略的性能,并分析了正常程序(如SPEC CPU 2006)中S-Pattern的比例。 +\begin{figure}[htbp] + \centering + \includegraphics[width=0.8\textwidth]{tpbuf.eps} + \caption{TPBuf 的结构\supercite{conditional-speculation}} + \label{fig:tpbuf} +\end{figure} - TPBuf旨在通过所有推测执行的S-Pattern捕获内存访问行为。 它记录所有动态推测存储器访问请求并跟踪它们的执行状态(例如,是否重新填充所请求的高速缓存行)。 当一个错过L1 DCache的新内存请求时,TPBuf会将其页面地址与其历史记录进行比较。 并且它根据表II中描述的逻辑决定这种新的推测指令是否安全。 - TPBuf的微体系结构如图4所示。一个主要的设计原则是尽可能利用现有逻辑来降低实现的复杂性,例如避免TPBuf成为核心管道内的新时序关键路径。 TPBuf靠近加载存储队列(LSQ),其条目与LSQ的条目具有1:1映射。 TPBuf条目的分配,提交和压缩与LSQ的Head和Tail指针的移动一起操作。此外,TPBuf涵盖了推测执行窗口中的所有动态推测存储器指令。为了防止攻击者直接推测性地访问未授权数据然后将数据传播到他自己的存储空间,必须首先检查访问地址并使用TLB获取物理页码(PPN)。 TPBuf记录并使用PPN作为每个条目的标记。此外,每个TPBuf条目都存储一个掩码和许多状态位。 TPBuf检测S模式并将结果传递给Cache-hit过滤器,该过滤器决定是否应该阻止可疑的推测性未命中请求。这样,原始内存一致性模型和缓存一致性不受影响。 TPBuf的查找如图4所示。 +TPBuf 的结构如图\ref{fig:tpbuf}所示。TPBuf 的条目和 LSQ 的条目一一对应, +它的条目分配、提交等操作和 LSQ 一起进行。TPBuf 包含了所有进行中的推测式 +的访存指令。为了避免攻击者访问未授权的数据,并将其传播到自己的地址空间, +需要先用 TLB 获得访问的物理地址,将其写入 TPBuf 中该访问对应的一 +项。TPBuf 检测 S-pattern,并将结果再传至缓存命中过滤器,决定是否阻止一 +个可疑推测式执行的请求。 -分配:当在LSQ中分配存储器访问指令时,它们也在TPBuf中分配并且A位被置位。并且根据TPBuf中的A位生成掩码。 -它指示TPBuf中的哪些内存指令比程序顺序中的新条目旧。 +% 分配:当在LSQ中分配存储器访问指令时,它们也在TPBuf中分配并且A位被置位。并且根据TPBuf中的A位生成掩码。 +% 它指示TPBuf中的哪些内存指令比程序顺序中的新条目旧。 -更新:使用存储器指令附带的可疑推测标志更新S位。当PPN记录在TPBuf中时,V位置位。当存储器指令获取的数据可供其他指令使用时,W置位。 +% 更新:使用存储器指令附带的可疑推测标志更新S位。当PPN记录在TPBuf中时,V位置位。当存储器指令获取的数据可供其他指令使用时,W置位。 -检测:当传入请求进入TPbuf时,TPBuf将其PPN与现有条目的PPN进行比较,然后生成地址匹配向量(匹配)。这些矢量,包括Match,V,W和S,用作等式1逻辑的输入,以确定请求是否安全。特别地,“|”表示减少OR,它对向量中的所有位进行OR运算以生成1位输出。 +% 检测:当传入请求进入TPbuf时,TPBuf将其PPN与现有条目的PPN进行比较,然后生成地址匹配向量(匹配)。这些矢量,包括Match,V,W和S,用作等式1逻辑的输入,以确定请求是否安全。特别地,“|”表示减少OR,它对向量中的所有位进行OR运算以生成1位输出。 %%%%%%% -在 gem5 模拟器中用 SPEC CPU2006 进行评测,平均性能开销为 6.8\%. +在 gem5 模拟器中用 SPEC CPU2006 进行评测,使用基于缓存命中的过滤器时, +平均性能开销为 12.8\%,再加上 TPBuf 过滤器,平均性能开销可以再降低至 +为 6.8\%. \subsubsection{SpectreGuard} -SpectreGuard\supercite{spectreguard} 是一种软硬件结合防御 Spectre 攻击 -的方法。软件在页表中标记一个页是否存在秘密数据,如果一条指令在推测式执 -行中读取了带标记的页中的数据,处理器则禁止这条指令将读取的值转发至其他 -指令,直到这条指令之前的所有预测都已验证,确认推测式执行正确。 - -%%% gtran %%% - -\Todo: SpectreGuard introduction - -\Fixme: 重新翻译 - -自Spectre披露以来,已经提出了几种基于软件和硬件的缓解策略。基于软件的缓解策略通过手动插入序列化指令[12]或在条件跳转和后续存储器加载指令之间引入其他数据依赖性[7,21]来防止推测。但是,手动识别程序的易受攻击的分支是困难的,而通过编译器自动化保护所有分支会导致过多的性能开销[21]。基于硬件的方法通常通过引入额外的硬件结构来缓冲推测结果来集中隐藏攻击者可观察的微架构状态变化[13,27]。虽然它们不需要手动更改代码,但它们会导致侵入式硬件更改和高性能开销[13,27]。 - -在本文中,我们提出了SpectreGuard,一种针对幽灵攻击的新型跨层防御。我们的方法是以数据为中心的,因为我们专注于程序的数据而不是代码。我们观察到,对于软件开发人员来说,识别程序的秘密数据(例如,密钥)可能比识别易受攻击的代码块(即,幽灵小工具)更容易,这些代码块可以出现在程序的任何分支中,即使它们不相关处理秘密数据。因此,我们的方法首先要识别敏感的内存块,这些内存块保存秘密数据,并将它们标记为非推测性内存区域。识别的存储区域通过简单的OS /库API被通知给OS,然后由硬件利用以通过低成本的微架构扩展选择性地和有效地防止推测性攻击。 - -我们的微架构扩展很小,并且基于一个基本观察,即成功的Specter变体1攻击需要以下三个不同的步骤推测性地发生:(步骤1)秘密数据从存储器层次加载; (步骤2)然后将其转发给相关指令; (步骤3)执行从属指令,通过微架构隐蔽信道(例如,高速缓存)泄漏秘密。重要的是要注意秘密是通过第二和第三步骤泄露的,在此期间留下了攻击者可观察的,秘密依赖的,微观建筑的足迹。换句话说,即使秘密数据被加载到CPU管道上,除非它被转发到秘密相关指令,否则秘密不会泄露给攻击者。 - - 基于这种观察,我们的方法允许第一步发生但延迟第二步直到所有先前的分支被解决之后。请注意,仅当第一步中的虚拟地址在非推测内存区域内时才需要此延迟。所有其他“正常”地址可以立即转发到任何等待的相关指令。因此,不经常访问的秘密将在整体性能上具有可忽略的开销。 - -%%%%%%%%%%%%%% - -在 gem5 模拟器中用 SPEC CPU2006 评测,如果标记内存的所有页,则 -SpectreGuard 的平均性能开销为 20\%, 而如果标记堆区域,则性能开销减少至 -8\%. 在使用 OpenSSL 的合成基准测试中,如果只标记私钥为秘密数据,则性能 -可以接近原处理器的性能。 +SpectreGuard\supercite{spectreguard} 是一种以数据为中心的软硬件结合 +的Spectre 防御方案。这种方案考虑到对于软件开发人员来说,识别程序中的秘 +密数据(如密钥)比识别可能受到攻击的 Spectre 组件代码容易。因 +此,SpectreGuard 在操作系统中提供接口,使程序可以标记敏感的内存区域,硬 +件上只需要简单的微架构扩展,就可以阻止 Spectre 攻击。和已有的防御方案相 +比,它不需要依靠编译器识别程序中可能受攻击的代码,也不需要复杂的硬件修 +改。 + +Spectre 攻击需要分为三个步骤:从存储系统装载秘密数据,将秘密数据转发至 +依赖于这个数据的指令,执行依赖于这个数据的指令,通过微架构隐蔽信道泄露 +秘密。数据的泄露在第二、三步进行,如果秘密数据被装载,但不被转发,秘密 +也不会泄露给攻击者。 + +因此,SpectreGuard 允许第一步发生,但将第二步延迟至装载秘密数据的指令 +之前所有分支均已解决。并且,在经过软件标记之后,第一步中仅当数据在敏感 +内存区域时,才需要延迟转发。当秘密数据不经常访问时,总体开销可以忽略。 + +敏感数据的标记通过在页表中添加一个位实现,在软件中可以扩展操作系统 +的mmap, mprotect 等接口,或通过运行库、编译器、连接器实现。在微架构中, +指令执行后,执行结果将转发至后续依赖于这个指令的指令,而如果指令是访问 +了敏感区域的指令,则推迟它的转发。 + +在 gem5 模拟器中用 SPEC CPU2006 对几种标记方案进行评测,方案 SG(All) 标 +记内存的所有页,平均性能开销为 20\%, SG(Heap) 只标记堆区域,性能开销减 +少至8\%. 在使用 OpenSSL 的合成基准测试中,如果只标记私钥为秘密数据,则 +性能可以接近原处理器的性能。 % \begin{comment} % |