\chapter{Meltdown 和 Spectre 攻击的防御方案及分析}\label{sec:defense}

Meltdown 和 Spectre 及其多种变体被发现后，研究者提出了多种减轻这些攻击
的软硬件防御方法。本章讨论并分析已有的防御方法。

\section{Meltdown型攻击的防御}

Meltdown 型攻击利用了暂态指令可以读取体系结构层次上不可访问的数据，并且
用此数据做计算。因此一种防御方式是使体系结构层次上不可访问的数据，在微
架构层次上仍然不可访问。

KAISER\supercite{kaiser} 是一种已经部署在 Linux 内核上的一种防御
Meltdown 攻击的方案。它的作用是在用户空间中去除内核空间的地址映射，使
得用户空间的执行的指令无法访问内核空间的存储区域。

防御 Meltdown 型攻击的第二种方法是阻止异常的产生。例如对于 LazyFP，最
新的 Linux 内核在上下文切换时，对所有进程都会保存和恢复浮点寄存器，从
而用户程序使用浮点寄存器都不会产生异常，从而消除这种攻击。

\section{Spectre型攻击的防御}

Spectre 型攻击的防御方案可以分为三类：阻止推测式执行、防止暂态指令访问
秘密数据、切断隐蔽信道或降低隐蔽信道的精度。研究者在软件和硬件方面都提
出了防御方案。

\subsection{软件防御方案}

Intel 和 AMD 都提出了在分支指令后插入 lfence 指令阻止推测式执行的方
法。\supercite{intel-spectre}\supercite{amd-spectre}lfence 指令在此作
为一条串行化指令使用，可以在 lfence 指令提交前阻止新的指令执行，从而阻
止了程序在推测式执行中对秘密数据进行操作。

由于 lfence 性能开销大，LLVM 提出推测式装载加固（Speculative Load
  Hardening）\supercite{spec-load-hardening} 技术，它的做法是将可能为
Spectre 组件的代码转为图 \ref{fig:SLH} 中的代码。

\begin{figure}[htbp]
  \centering
\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}，它的
功能如图\ref{fig:array-index-nospec}：

\begin{figure}[htbp]
  \centering
\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{Linux 中的索引掩码}
\label{fig:array-index-nospec}
\end{figure}

如果数组索引 index 小于 size，则 mask 为一个所有位为 1 的值，产生的索
引不变，否则 mask 为 0，产生的索引为 0，从而推测式执行时的数组索引始终
在数组大小范围内。

% poison value
Webkit 还使用了指针投毒（pointer poisoning）\supercite{webkit}的方法，
它考虑分支用于做类型检查的情形。对于不同类型的数据，Webkit 将这种类型的
指针和一个类型对应的值异或，要使用的时候再异或这个值得到指向数据的指针，
如果类型不匹配，则得到的指针会指向一个程序无法访问的内存区域。通过这种
方法，程序在推测式执行中无法通过这种指针访问到程序中的数据，从而避免了
秘密数据的泄露。

% site isolation
Google 为 Chrome 浏览器使用了站点隔离（site isolation）技术，使得浏览
器用不同的进程访问不同的网站，从而攻击者无法通过用 Javascript 通过侧信
道攻击获取另一站点相关的数据。\supercite{here-to-stay}

% timer reduction
降低计时器精度可以降低计时攻击所用侧信道的精度，一个例子是在浏览器中降
低 Javascript 引擎中 performance.now 等计时器的精度。
\supercite{webkit} 但是相关研究发现，攻击者可以使用其他方式构造高精度
的计时器\supercite{js-timer}，因此降低计时器精度不是一个有效的防御方法。


\subsection{硬件防御方案}

由于软件防御方案可防御的攻击类型有限，并且性能开销大，因此研究人员提出
了多种硬件防御方案。

\subsubsection{SafeSpec}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% gtran %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%\Todo: SafeSpec III. SAFESPEC :LEAKAGE - FREE SPECULATION 一章，包含一
%\Fixme: 需要重新翻译

SafeSpec\supercite{safespec}提出了一种设计原则保护处理器免受推测式执行
攻击，同时保持推测式执行带来的性能提升。SafeSpec 的总体设计如图
\ref{fig:safespec}所示，使用临时结构（图中的影子状态）来保存任何推测式
执行的指令产生的状态，而不影响处理器的主要结构（在图中称为提交状态）。
例如，如果推测式执行中的装载指令需要向缓存中填入一个缓存行，则改行保存
在临时结构中，而不是高速缓存。如果这个指令被取消，则它产生的效果也一起
被清除，从而错误的推测式执行不会改变缓存状态；如果指令提交，则将这个缓
存行从临时结构移动至缓存中。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{safespec.eps}
  \caption{SafeSpec 总体设计}
  \label{fig:safespec}
\end{figure}

虽然SafeSpec原则上很简单，但必须解决许多与其安全性，复杂性和性能相关的
问题。

将状态从影子状态移动到提交状态有两种选择。第一种选择是等待分支（Wait
  For Branch，WFB），当一个指令依赖的所有分支结果都已经得出时，这个指
令不再是推测式执行的指令。WFB 可以防御 Spectre v1 和 v2,它们依赖于错误
的分支预测，没有任何错误的推测式执行产生的状态进入提交状态。而
Meltdown 不依赖于分支预测，因此 WFB 不能防御 Meltdown. 第二种选择是等
待提交（Wait For Commit，WFC），在指令提交的时候，才将推测式执行产生的
状态更新至提交状态。

影子状态的大小是另一个设计选择。如果保存影子状态的结构太小，则会导致推
测式执行的状态被替换，或指令需要在发射前停顿，直到影子状态有足够的空间。
因此，影子状态应当设计为能符合一般程序产生的推测式执行状态数量的大小。
暂态推测式攻击从安全的角度，说明影子状态的大小要足够大。

SafeSpec 防止推测式执行的产生的值影响提交状态，从而阻止了 Spectre 攻击
通过隐蔽信道传送数据。但是 SafeSpec 并没有隔离推测式执行中的指令的状态，
这可能造成暂态推测式攻击（Transient Speculation Attack）。由于指令在提
交前可在推测式执行的状态，在一段时间内，它可以和错误推测式执行的指令共
享状态。

例如，考虑一个非常小的影子结构（如只有一个条目），恶意的推测式执行的代
码可以读取一个特权数据，隐蔽地通过影子状态传送这个数据至接收者。它可以
替换影子状态中的条目，使得接收者发现其推测式执行状态丢失。如果在影子状
态满时阻塞执行，接收者可以发现它的执行时间变长。

%SafeSpec 的一个问题是如果一个长延迟指令，例如从内存中读取数据的指令，
%在执行过程中被取消，它的状态需要丢弃。因此，如果一个长延迟指令的结果返回，
%而没有匹配的事务，则丢弃这个结果。

SafeSpec 原则可以用于在推测式执行中保护缓存和 TLB，方法是在处理器中加
入以下影子结构：

\begin{enumerate}
\item 数据缓存：在已有的 Spectre 攻击中都用到了数据缓存作为隐蔽信道。
  它的影子结构保存了推测式执行中获取的缓存行。在装载/存储队列中，将获
  得了数据的推测式执行的装载指令指向影子结构的对应项，在同一个分支下访
  问同一行缓存的指令可以使用影子结构中的内容。指令提交时，缓存行从影子
  结构移动至缓存。如果指令被丢弃，则释放影子结构对应的一项。在这种设计
  中，被丢弃的推测式执行的装载指令不但不会修改缓存中的内容，也不会改变
  缓存替换算法的状态。
\item 指令缓存：构造一种利用指令缓存的 Spectre 攻击是可能的，因此指令
  缓存也需要保护，方法和数据缓存类似。
\item TLB：TLB 也可以作为隐蔽信道使用，攻击者可以检查访问一个页的时间
  判断 TLB 是否命中。现代处理器进行地址翻译是个复杂的过程，在访问页表
  时，页表项也会带入数据缓存，这个过程也使用装载存储队列，因此数据缓存
  的保护足够保护这个过程。因此对 TLB 的保护只需要添加 TLB 本身的影子结
  构。
\end{enumerate}

使用 MARSSx86 模拟上述 SafeSpec 模型，可以发现运行 SPEC2017 有平均 3\%
的性能提升。性能提升的原因是影子结构增大了有效缓存的容量，避免了错误推
测式执行路径污染缓存。

\subsubsection{InvisiSpec}

InvisiSpec \supercite{invisispec} 使推测式执行产生的副作用在数据缓存中
不可见，从而可以防御利用推测式执行的攻击。它的目标是阻止由推测式执行的
装载操作产生的微体系结构侧信道和隐蔽信道，如缓存占用、缓存行替换算法信息、
缓存一致性状态等。和 SafeSpec 不同，InvisiSpec 支持多处理器。

Spectre 等攻击由暂态指令引起，为了定义 InvisiSpec 的攻击模型，
表\ref{tab:transient_insn}列出了各种攻击中暂态指令的来源。InvisiSpec 关
注基于缓存的侧信道和隐蔽信道，因此只考虑装载指令。InvisiSpec 不但要防
御 Spectre 攻击，并且还要防御未来的攻击。未来的攻击中有多种暂态指令的来
源。根据这两类攻击，InvisiSpec 设计了两种模型模
型：Spectre 和 Futuristic.

\begin{table}
\begin{tabular}{|c|c|}
\hline 
攻击 & 暂态指令的来源\tabularnewline
\hline 
\hline 
Meltdown & \multirow{2}{*}{虚拟内存异常}\tabularnewline
\cline{1-1} 
L1TF & \tabularnewline
\hline 
LazyFP & \multirow{2}{*}{读取禁用的或者特权寄存器发生异常}\tabularnewline
\cline{1-1} 
Rogue System Register Read & \tabularnewline
\hline 
Spectre & 控制流预测错误\tabularnewline
\hline 
Speculative Store Bypass & 装载指令和更早的存储指令地址别名\tabularnewline
\hline 
未来的攻击 & 异常、控制流预测错误、访存别名、一致性违例、中断等\tabularnewline
\hline 
\end{tabular}
\caption{暂态指令的来源}
\label{tab:transient_insn}
\centering
\end{table}

InvisiSpec 的微体系结构基于两种机制。第一个机制中，不安全的推测式执行的装载
操作将数据读入至推测式执行缓冲区（Speculative Buffer，SB）中，而不读入
缓存，从而不修改缓存存储层次的状态。由于缓冲区中的数据不产生缓存一致性
事务，因此可能造成内存一致性违例。第二个机制解决这个问题，当推测式执行
的装载操作变得安全时，处理器将这个操作重新发射至存储系统，使其对整个系
统可见。如果这个装载指令可能造成访存违例，InvisiSpec 验证读出的指令是
否正确，如果验证失败，则重新执行这个访存指令及其后续指令。
% squash the load

InvisiSpec 在不同的内存一致性模型下，会使用不同的执行策略。内存一致性模
型（或内存模型）指定在一个共享内存的系统中，处理器核执行访存操作，并使
其对其他处理器核可见的顺序。在存储指令执行结束后，存储的数据存放在写缓
冲区中，随后写入缓存。当写操作写回缓存后，其他核可以看见这个数据，存储
操作在此时完成。而装载操作在提交前从存储系统读取数据，当它得到数据时，
则称为装载操作完成。装载操作的完成可以在程序序列中其他的访存操作之前，
可以导致内存一致性违例，此时处理器需要用恢复机制重新执行这个装载指令及
其后的指令。

常见的内存模型有 TSO（Total Store Order） 和 RC （Release Consistency）。
TSO 是 x86 体系结构所用的内存模型。TSO 允许一个装载操作在不同地址的存
储操作之前完成，除此之外禁止所有可见的装载和存储的重排序。在实现中，为
了防止可见的装载指令的重排序，需要确保一个装载操作读入的数据，在提交时
仍然有效。如果处理器核遇到一个外部的缓存失效请求，则需要重新执行这个装
载操作。存储指令的顺序由先入先出的写缓冲区保证。RC 则允许同步指令之外
的任何访存操作的重排序。

InvisiSpec 关注不安全的推测式执行的装载指令（Unsafe Speculative
Load，USL），在 Spectre 的攻击模型中，USL 是在未决分支之后的装载指令，
在得到分支结果后，正确路径上的 USL 转为安全的装载指令。而在未来攻击的模
型中，USL 变为安全仅当指令到达 ROB 头部，或不被可能中断该指令的事件回
卷。

InvisiSpec 的目的是使 USL 在缓存层次结构中不可见，这意味着 USL 不可以用
其他线程可见的方式修改缓存层次结构，包括一致性状态。到了一个时间
点，USL 可以转为安全的装载操作，这个时间称为可见时间点（Visibility
Point），这时其他线程可以看到这个操作在存储系统中的效果。InvisiSpec 通
过重新装载数据，将数据存入本地缓存，改变缓存层次结构的状态。

在 USL 发射至可见的时间段里，它不改变任何缓存一致性状态，处理器核可能无
法接收对 USL 对应的缓存行的失效请求，从而检测不到这个装载指令对内存一致
性模型的违例。为了解决这个问题，InvisiSpec 需要进行一个验证的步骤，它在
可见时间点重新装载数据。

USL 请求的缓存行在 USL 发射时有可能已经在一级缓存中，这个缓存行也被载
入推测式执行缓冲区。处理器可能接收对这一行的失效请求，但是 USL 会忽视
这个请求，这个失效请求的效果会在可见时间点处理。

%\Todo: InvisiSpec  V: INVISISPEC: THWARTING SPECULATION ATTACKS

%\Fixme: 重新翻译以下内容

%1）不安全的推测负载：严格地说，任何在到达ROB头部之前启动读取的负载都是推测负载。在这项工作中，我们对可能由于推测而产生安全漏洞的（大）投机负载子集感兴趣。我们称这些负载为Unsafe Speculative Loads（USLs）。作为USL的一组推测性负载取决于攻击模型。

%在Spectre攻击模型中，USL是遵循未解决的控制流指令的推测性负载。一旦控制流指令解决，跟随它的USL在分支的正确路径中转换到安全负载 - 尽管它们仍然是推测性的。

%在我们的未来攻击模型中，USL是所有可以被先前指令压扁的推测性负载。一旦USL变为（i）非推测性的，因为它到达ROB的头部或者（ii）推测不能通过任何先前的指令（或推测性的不可压缩的短路）变为安全负载。推测性非可压缩负载是（i）不在ROB的头部并且（ii）在ROB之前仅通过指令而不是由表I中的任何压扁事件压缩的未来的负载。请注意，在表中，其中一个压缩事件是中断。因此，使负载安全包括延迟中断直到负载到达ROB的头部。

%要了解为什么这两个条件允许USL过渡到安全负载，请考虑以下内容。 ROB头上的负载本身不能是瞬态的;它可以被压扁（例如，由于异常），但它是一个正确的指令。关于推测性非可压缩负载也可以这样说。这是因为，虽然不在ROB头部，但是它们不能被任何先前的指令压扁，因此可以被认为是ROB头部处的指令的逻辑扩展。

%2）使USLs看不见：InvisiSpec背后的想法是让USL隐形。这意味着USL无法以任何其他线程可见的方式修改缓存层次结构，包括一致性状态。 USL将数据加载到我们称为Speculative Buffer（SB）的特殊缓冲区中，而不是加载到本地缓存中。如上所述，USL可以转换到安全负载的时间点。此时，称为可见性点，InvisiSpec采取的行动将使USL可见 - 即，将使USL在内存层次结构中的所有副作用对所有其他线程都明显。 InvisiSpec通过重新加载数据使USL可见，这次将数据存储在本地缓存中并更改缓存层次结构状态。负载可能仍然是推测性的。

%3）维护内存一致性：负载的可抑制可见性窗口是从USL发出负载到使其自身可见之间的时间段。在此期间，由于USL不会更改任何一致性状态，因此核心可能无法接收针对USL加载的线路的无效。因此，负载违反内存一致性模型存在未检测到的风险。这是因为这种违规通常是通过传入的失效来检测的，并且通过压缩负载来解决。要解决此问题，InvisiSpec可能必须在负载的可见点重新加载数据时执行验证步骤。

%当USL发布并且线路加载到SB时，USL请求的线路可能已经在核心的L1缓存中。在这种情况下，核心可能会收到该行的无效。但是，USL忽略了这种失效，因为USL是不可见的，并且这种失效的任何影响都将在可见点处理。

在可见时间点重新装载数据，有两种操作形式。第一种方式是验证
（Validation），它将 USL 用到的数据，和存储系统中这些数据的最新的值做
比较，如果不相同，USL 和其后的所有指令都需要重新执行。这个操作用于满足
内存一致性模型，方法类似于基于值的内存保序方法。
\supercite{cain-lapasti}

%如果在该窗口期间，核心已经收到USL加载的行的无效，则验证是在可抑制可见性窗口期间（由于内存一致性考虑）使可见的USL可见的方法。验证操作包括将USL使用的实际字节（存储在SB中）与从缓存层次结构加载的最新值进行比较。如果它们不相同，USL及其所有连续指令都会被压扁。这是满足内存一致性模型所必需的。这一步让人想起Cain和Lipasti的基于价值的记忆排序[31]。

USL 在验证阶段不能提交，需要等待验证结束，在这个时间段需要从存储系统获
取缓存行并写入缓存，比较推测式执行缓冲区和读入的缓存行中，USL 所要使用
的数据。如果 USL 在 ROB 队头并且 ROB 已满，会导致流水线停顿。

为了降低验证操作的开销，对部分 USL，InvisiSpec 使用另一种操作，称为曝
光（Exposure），它用于在 USL 可见之前，不会违反内存一致性模型的 USL。
这个操作只需要将读取到的一行数据存入缓存，当请求发送至存储系统后，指令
即可提交，从而不会使流水线停顿。

%值得庆幸的是，InvisiSpec识别出许多在其抑制可见性窗口期间无法违反内存一致性模型的USL，并允许它们以廉价的曝光可见。 如果在该窗口期间核心已经收到USL加载的行的无效，则这些USL在禁用可见性窗口期间不会被内存一致性模型压扁。 在曝光中，缓存层次结构返回的行只是存储在缓存中而不进行比较。 一旦将行请求发送到缓存层次结构，持续曝光的USL就会退出。 因此，USL不会阻止管道。

%总而言之，有两种方法可以使USL可见：验证和曝光。 内存一致性模型确定需要哪一个。 图2显示了具有验证和曝光的USL的时间线。

%InvisiSpec的负载有两个步骤。首先，当它作为USL发布到内存时，它访问缓存层次结构并获得所请求的缓存行的当前版本。该行仅存储在本地SB中，本地SB与L1高速缓存一样靠近核心。 USL不修改高速缓存一致性状态，高速缓存替换算法状态或任何其他高速缓存层次结构状态。没有其他线程（本地或远程）可以看到任何更改。但是，核心使用USL返回的数据来取得进展。 SB存储行而不是单个词来利用空间局部性。

%当可以使USL可见时，并且总是在它收到其请求的高速缓存行之后，硬件触发验证或暴露事务。这样的事务再次重新请求该行，这次修改缓存层次结构，并将该行带到本地缓存。如第V-A4部分所述，验证和暴露交易的运作方式不同，并且具有不同的性能影响。

InvisiSpec 针对 Spectre 和 Futuristic 两种攻击模型的设计略有不同。对
于 Spectre 攻击模型，USL 在此前所有的控制指令得出结果后，则进入可见时
间点，此时处理器可以为这个指令执行验证或曝光操作。同时这些验证和曝光操
作在时间上可以重叠。

而对于 Futuristic 模型，USL 在两种情况下可以进入可见时间点：它到达
了 ROB 的队头，或者它不再可能被取消。当处理器执行验证操作时，其他验证或
曝光操作不可以与之重叠，因为验证和曝光操作会改变缓存的状态，而正在验证
的指令如果验证失败，会导致后续的指令被取消，重叠的验证或曝光操作可能泄
露数据至缓存状态。而对于曝光操作，直至下一个验证操作之前的所有曝光操作
均可以重叠执行。

总的来说，当验证操作阻止 ROB 队头的指令提交，并且 ROB 已满时，流水线的
停顿才可能发生。这种情况更可能发生在 Futuristic 模型中。

%我们将这些设计称为InvisiSpec-Spectre（或IS-Spectre）和InvisiSpec-Future（或IS-Future）。它们显示在表II的第一行中。为了进行比较，第二行显示了如何使用基于栅栏的方法防御这些相同的攻击 - 遵循当前提出的防御幽灵的建议[32]。我们将这些设计称为Fence-Spectre和Fence- Future。前者在每个间接或有条件的分支后放置围栏;后者在每次装载前放置围栏。

和基于 fence 的方法相比，InvisiSpec 性能更优，因为和传统的不安全的机器
相比，装载指令执行的时间不变。InvisiSpec 有几个影响性能的问题。一个是
验证操作造成的流水线停顿，通过将更多的验证操作转化为曝光操作，可以减少
流水线的停顿。另一个问题是，InvisiSpec 会在存储系统中产生更大的流量和
缓存端口的竞争。

对于 TSO 内存模型，如果一个装载指令之前不存在更早的装载指令或者 fence，
则它不会被对它读取的行的失效请求取消，因此，这些 USL 在可见时间点可以使
用曝光操作。相反，如果一个 USL 之前有其他的装载指令，则需要执行验证操
作。而对于 RC 模型，只有在 USL 之前有 fence 的情况下，才需要执行验证操
作。
% TODO!!!

%从这个讨论中，我们看到在TSO下观察停止管道验证的机会最高的设计是IS-Future。为了减少这些事件的发生，InvisiSpec实现了两种机制。第一个允许一些使用验证的USL代替使用曝光。第二个标识可以使用验证的USL，如果验证失败的可能性很大，则提前将它们压扁。我们接下来考虑这些机制。

%1）将验证USL转换为曝光USL：假设在TSO下，USL1在ROB中存在较早的负载时启动读取。通常，InvisiSpec会将USL1标记为需要验证。但是，假设在读取时，早于USL1的ROB中的所有负载已经获得了他们请求的数据 - 特别是，如果它们是USL，则他们请求的数据已经到达SB并且已经到达传递到登记册。在这种情况下，USL1不会相对于任何早期的负载重新排序。因此，TSO不会要求在收到对其加载的线路无效时压缩USL1。因此，USL1被标记为需要曝光，而不是验证。

%为了支持这种机制，我们用一个名为Performed的位来标记每个USL。当USL请求的数据已在SB中接收并传递到目标寄存器时设置。

%2）USL的早期压缩需要验证：假设核心接收到其缓存中的行的失效，该行也恰好被标记为需要验证的USL加载到其SB中。接收失效表示该行已更新。此类更新通常会导致USL在可见性失败时的验证。如果此失效是由错误共享引起的，或者如果对该行的所有更新的净效果直到验证结果是无声的（即，它们将数据恢复到其初始值），则验证才能成功。由于这些条件不太可能发生，InvisiSpec会因接收失效而挤压这样的USL。还有第二个USL的案例，验证失败的可能性很高。假设USL1需要验证并且在SB中有数据。此外，有一个较早的USL2到同一行（但对于该行的不同单词），其在SB中也有其数据并需要验证。当USL2执行验证并将线路连接到核心时，InvisiSpec还会将该线路与SB中的USL1数据进行比较。如果数据不同，则表明USL1已读取陈旧数据。那时，InvisiSpec保守地压制了USL1。

  % skips subsection D, E

一个 USL 执行两个内存访问，一个在首次发射的时候，另一个在验证或曝光的
时候。如果第一个访问在末级缓存缺失并访问主存，则第二个访问也很可能访问
主存。
为了提高性能，InvisiSpec 为每个核添加一个末级缓存推测式执行缓冲区
（LLC-SB），USL 的第一个访问如果来自主存，则将获取的一行同时放入LLC-SB，
此后在验证时，就可以从 LLC-SB 获取数据，而无需访问主存。而在另一个核访
问了这一行时，为了防止 LLC-SB 获取过时的数据，才需要使 LLC-SB 中的这一
行失效，验证和曝光操作将会从存储系统中存有最新数据的地方获取数据。

在 gem5 模拟器下， 使用 SPEC CPU2006 和 PARSEC 对 InvisiSpec 进行评测，
在 TSO 内存模型下，以 Spectre 为威胁模型，InvisiSpec 平均性能损失为
21\%，而使用 fence 性能下降 74\%.

InvisiSpec 的性能开销来源于三个方面，一个是验证导致的流水线停顿，在大
多数程序中，这部分开销可以忽略；第二个是取消了错误执行路径后，它装载的
数据无法被后续的指令复用；第三个是每个 USL 需要两次存储访问，造成资源
的竞争。

InvisiSpec 会增大存储系统中各个缓存之间，和缓存和主存之间的网络流量，
在 TSO 下，以 Spectre 为威胁模型，相对于原处理器，流量增大 34\%.

\subsubsection{DAWG}

隐蔽信道是在已有的保护机制下，在不允许通信的进程之间传输信息的信道。例
如，当侧信道用于向攻击者传送秘密数据时，这种攻击包含一段在受害者保护域
内执行的代码，用于访问秘密数据，以及一个传送器，用于将秘密传递给攻击者。
这些部分构成了一个数据通道，它基于秘密信息调制这个信道。由攻击者控制，
并在受害者保护域之外的接收器，可以监听信道上的信号，并对其进行解码以确
定该秘密。图\ref{fig:dawg-schema}描述了这种攻击模式。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{dawg-schema.eps}
  \caption{不同安全域间通过隐蔽信道传送秘密数据}
  \label{fig:dawg-schema}
\end{figure}

经典的对 RSA 的攻击\supercite{RemoteTA}中，代码使用了一段依赖于秘密的
执行序列，将私密信息通过执行序列传送到了指令缓存状态中，这造成了一个攻
击者可以推断秘密数据的隐蔽信道。在这种情况中，访问秘密数据和传送秘密数
据的代码均在已有的 RSA代码中。共享指令缓存的攻击者可以用一个接收器解码
来自缓存信道的信息。

Meltdown 和 Spectre 提供了新了构造数据通道的方式。通过利用错误的推测式
执行，攻击者可以使处理器执行通常无法访问的代码路径。在 Meltdown 中，攻
击者推测式执行一段代码，作为数据通道，它可以非法访问秘密数据，并在异常
发生之前通过微体系结构副作用传输至接受者。在 Spectre 中，攻击者使分支预测器
预测错误，使处理器沿着攻击者选择的路径进行推测式执行，在受害者中形成了数
据通道。因此，构建数据通道有三种方法：

\begin{enumerate}
\item 数据通道在受害者的代码中已经存在，例如对 RSA 的攻击
\item 攻击者编写了这个数据通道，如 Meltdown
\item 攻击者从受害者已有的代码中合成数据通道，如 Spectre
\end{enumerate}

这个框架可以用于描述除缓存状态之外的侧通道，例如通过分支预测器或 TLB 状态造
成的数据泄露。在未来，可能有更多种类的构造数据通道的方法。

DAWG（Dynamically Allocated Way Guard）\supercite{dawg} 是利用保护域之
间的强隔离防止数据被攻击者提取的方法，它阻止了任何一对传送器/接收器共享相同的信
道。这是一种用于缓存等组相联结构设计的的安全路划分机制。DAWG 为组相联的
结构添加保护域信息，以提供强隔离。和 Intel CAT \supercite{IntelCAT}不同，DAWG 不允许跨保护
域发生缓存命中。

DAWG 的目标是阻止任何攻击者和受害者安全域之间的基于缓存的信道。实现方
式是将所有缓存状态的修改隔离在一个保护域内，从而任何受害者域中的传送器
均不能连接至攻击者域中的接收器，从而阻止了受害者到攻击者的数据泄露。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.6\textwidth]{dawg-cache.pdf}
  \caption{DAWG 缓存设计\supercite{dawg}}
  \label{fig:dawg-cache}
\end{figure}

组相联缓存包含多个路，每一路是一个直接映射缓存。DAWG 将这些路分组，分
配到不同的保护域，将缓存请求所属保护域的缓存命中和行替换，限制在所分配
的路之内。在此之上，缓存所带的元数据，如替换策略的状态，也要在保护域内
定义，并安全地划分。

图\ref{fig:dawg-cache}展示了 DAWG 缓存的结构。处理器中增加几个 MSR，用
于记录处理器当前所在的保护域，和保护域的策略。每个逻辑核在访问存储器时
均带有一个域标识字段。

除了硬件修改之外，DAWG 还需要软件的支持，用于管理程序的保护域，和每个
域对应的安全策略。

在 zsim 模拟器中使用 SPEC CPU2006, PARSEC, GAPBS 进行模拟，相对于
Intel CAT，在不同的评测程序和划分方式下，大多数程序性能下降为 4\% 至 7\%.

\subsubsection{Context-Sensitive Fencing}

%\Todo: CSF introduction

%\Fixme: 重新翻译

% 为了在对性能影响最小的情况下执行安全的微码定制，这项工作利用了上下文敏
% 感解码（CSD）[68]，这是最近提出的对英特尔（和其他人）微操作转换机制的
% 扩展，可实现按需和上下文敏感自定义动态微操作指令流。此外，这项工作还利
% 用了CSD提供的重新配置框架，允许操作系统和其他可信实体动态控制通过外科
% 手术插入微操作流的投机栅栏的频率，类型和行为，以确保投机安全执行。

% 这项工作分析了一套显着扩展的围栏，考虑了不同的可能的执法阶段和执法策略。
% 特别是，我们引入了一个新的栅栏，可以防止推测性更新缓存状态，同时在指令
% 的动态调度中具有最小的干扰。

% 上下文敏感的防护能够通过基于解码器级信息流跟踪的新型检测机制自动识别网
% 格插入点。由于处理器前端通常以比管线的其余部分高得多的速率搅拌指令，因
% 此解码器级的信息流跟踪易于频繁过度和不确定的情况。虽然由于栅栏插入的频
% 率增加而导致过度拉伸可能会损害性能，但是对于短暂的窗口来说，不确定会破
% 坏系统的安全性。通过早期低开销的错误检测和恢复机制解决这些挑战，本文进
% 一步确定了解码器级信息流跟踪作为一种有效的攻击检测机制的可行性。

% 这项工作进一步提出了新的微操作流程，保护分支预测器和返回地址堆栈，防止
% 跨不同保护域的错误训练和/或逆向工程。虽然与英特尔提出的间接分支预测器
% 屏障（IBPB）指令的精神相似，但这些微操作流程更广泛地应用于更广泛的分支
% 预测器和返回地址堆栈，并提供更细粒度的控制。

% \Todo: 更详细地介绍 CSF

Context-Sensitive Fencing(CSF)\supercite{context-sensitive-fencing} 是
一种微码级防御多种类型 Spectre 攻击的方法。它基于 Context-Sensitive
Decoding (CSD)\supercite{context-sensitive-decoding}，一种微码翻译机制
的扩展，用于动态按需自定义微操作指令流。CSF 利用 CSD，在微指令流中注入
fence 等微码，阻止不安全指令的推测式执行。

CSD 是一种根据执行上下文，把指令翻译为不同的自定义微码的技术。操作系
统、运行时系统等软件可以通过配置 MSR，设定不同的微码翻译模式。它可以在
不重新编译的情况下，使不安全的程序变为安全的程序，把面向性能优化的代码变为
面向功耗优化的代码。系统软件可以利用已有的微码更新功能将自定义的微码更新至
处理器中。CSD 可以通过多种事件触发自定义微码翻译模式。

CSF 由以下几个关键部件组成：
\begin{enumerate}
\item 微码自定义机制 CSD：使处理器精确地在指令流中插入 fence，减轻推测
  式执行中不期望的副作用
\item 译码级信息流追踪（DLIFT）框架：用于检测潜在不安全的执行模式，触
  发微码自定义机制
\item 错误训练防御：用于保护分支预测器、返回地址栈等部件
\end{enumerate}

\begin{table}
  \centering
\begin{tabular}{cccccc}
\hline 
fence & 作用位置 & 严格/宽松 & 不允许的指令 & 防御的攻击种类\tabularnewline
\hline 
LSQ-LFENCE & LSQ & 宽松 & 装载 & v1\tabularnewline
\hline 
LSQ-MFENCE & LSQ & 宽松 & 装载和存储 & v1,v1.1,v1.2\tabularnewline
\hline 
CFENCE & 缓存控制器 & 宽松 & 无 & v1\tabularnewline
\hline 
\end{tabular}
\caption{CSF 新定义的三种 fence}
\label{tab:csffence}
\centering
\end{table}

为了降低 fence 的性能开销，CSF 还提出了3种新的用于防御 Spectre 的
fence. 表 \ref{tab:csffence} 列出了它们的特性和防御的攻击种类。在
fence 操作的设计上有以下考虑：

\begin{enumerate}
\item 作用位置：x86 的 lfence 作用在指令队列阶段，使得后续的指令
  在lfence 提交前无法进入指令队列后的流水线结构。fence 作用在更晚的阶段
  的结构，可以减少 fence 对性能的影响，而保护的侧信道更少。
\item 严格和宽松的 fence：严格的 fence 会阻止所有指令越过 fence，而宽松
  的 fence 允许某些指令越过它们。例如 lfence 是个严格
  的 fence，而 sfence 允许存储指令之外的指令越过 fence，是个宽松
  的 fence.
\end{enumerate}

使用 CSF 和自定义的 fence，防御 Spectre 的开销在 8\% 以下。

\subsubsection{Conditional Speculation}

条件推测式执行（Conditional Speculation）
\supercite{conditional-speculation} 是一种检测推测式执行中的潜在的泄露
数据的指令，并阻止其执行的方法。为了检测可能在推测式执行中泄露数据的指
令，此工作提出安全依赖（security dependence）的概念，类似于数据依赖和
控制依赖，执行存在安全依赖的指令则产生安全冒险。这种新的依赖用于描述具
有潜在的泄漏微体系结构信息的安全风险的推测性执行的指令。对于信道 $c$，
指令 $j$ 安全依赖于指令 $i$，如果：

\begin{enumerate}
\item 在程序序列中，指令 $j$ 在 $i$ 之前
\item $j$ 在 $i$ 之前推测式地执行，并且 $j$ 会通过信道 $c$ 泄露数据
\end{enumerate}

因为信息可以从多种信道上泄露，所以安全依赖的定义指定了某种信道。针对缓
存信道，如果指令 $j$ 不修改缓存的内容，则它对于缓存信道不具有安全依赖，
尽管它可能会通过其他信道泄露某些信息。

\begin{table}
  \centering
\begin{tabular}{|c|c|c|}
\hline 
Spectre 变体 & 指令 $i$ & 指令 $j$\tabularnewline
\hline 
\hline 
Spectre v1 & 条件分支 & 访存\tabularnewline
\hline 
Spectre v2 & 间接转移 & 访存\tabularnewline
\hline 
Spectre v4 & 访存 & 访存\tabularnewline
\hline 
SpectrePrime & 条件分支 & 访存\tabularnewline
\hline 
\end{tabular}
\caption{Spectre 攻击中的安全依赖}
\label{tab:secdep}
\centering
\end{table}

根据以上定义，表 \ref{tab:secdep} 总结了 Spectre 攻击中的主要的安全依赖。
Spectre 攻击的安全依赖性来自两种情况，分支-访存依赖和访存-访存依赖。
在条件推测式执行的流水线中，在发射队列中引入了安全冒险检测，以识别具有
安全依赖的可疑的不安全指令。

\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]$：

\begin{align*}
Matrix[X,Y] {}={} & (IQ[X].opcode == Memory) \\
                  & \&{} (IQ[Y].opcode == Memory {} || {} IQ[Y].opcode == Branch) \\
                  & \&{} IQ[Y].valid \\
                  & \&{} !IQ[Y].issued
\end{align*}

这个公式的含义如下：X 在进入发射队列之前 Y 有效，则意味着 Y 在 X 之前；
对于多种 Spectre 变体，只需要检查访存指令是否依赖于先前的分支或访存指
令；如果在访存指令发出时，此前的分支或访存仍在等待，则认为新的访存指令
存在安全相关。

在实现中，图\ref{fig:cond-spec}展示了发射队列的三个阶段。第一阶段中，数
据依赖矩阵生成一个依赖向量；第二阶段中，该向量发送至年龄矩阵，用于选择
最老的可发射的指令；在第三阶段，处理器将被选择要发射的指令放进安全依赖
矩阵，查询它们的安全依赖性。当一个将要发射的指令存在安全冒险时，它会被
标上\emph{可疑推测式执行(suspect speculation)}标记。

在指令 X 发射后，更新向量寄存器（update-vector）中的对应位设为0，在下
一周期，依赖矩阵中X 对应的一列将会重置，意味着待发射指令和 X 的之间的
安全相关被清除。

一种简单的冒险消除方式是阻止发射队列中标记了可疑推测式执行指令的执行，
但是这种策略会导致性能下降。为了实现性能，安全性和透明性的平衡，条件推
测式执行提出了两种过滤机制，以找出错误识别的安全冒险。基于缓存命中
的冒险过滤器针对命中缓存的推测式执行指令，因为它们的推测式执行不会改变
缓存的内容，所以它们是安全的。基于可信页面缓冲区
（Trusted Page Buffer, TPBuf）的冒险过滤器，从另一个角度识别安全的推测
推测式执行的指令。对于使用基于共享内存（例如 Flush+Reload）的缓存侧通道
攻击窃取内存信息的 Spectre 变体，他们对恶意组件的推测式执行具有称
为\emph{S-模式}的共同特征。 TPBuf 从所有推测式执行中捕获S-模式，对于
任何推测式执行的访存指令，如果它与S-模式不匹配，则被认为是安全的。

% hit-based

考虑到大多数程序有良好的时间局部性和空间局部性，并且一级缓存命中的访存请求
不会修改缓存的内容，因此可以使用基于缓存命中的冒险过滤器，以减少阻止可
疑推测式执行的指令造成的性能损失。只要访存指令的访问命中一级缓存，则该
指令正常执行，但是如果缓存缺失，则该指令将会重新回到发射队列，直到安全
依赖得到解决，才可以重新发射。这个过滤器只需要对一级缓存的控制逻辑做少
量修改，即不处理标记为可疑推测式执行，并且不命中的访存请求。

% TPBuf
如果一个程序的一级缓存命中率低，则基于缓存命中的冒险过滤器仍然会阻止大
量的推测式执行。对于利用共享内存构造侧信道的 Spectre 攻击方式，并非所有
的一级缓存缺失都可作为隐蔽信道使用。因此可以使用基于 TPBuf 的冒
险过滤器，检测这些指令中安全的指令。在这些攻击中，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 中其他访问的页地址比较，如果有至少一个访问访
问了不同的页，并且正在写回阶段，则认为新的请求不安全。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.6\textwidth]{tpbuf.eps}
  \caption{TPBuf 的结构\supercite{conditional-speculation}}
  \label{fig:tpbuf}
\end{figure}


TPBuf 的结构如图 \ref{fig:tpbuf} 所示。TPBuf 的条目和装载存储队列（LSQ）的条目一一对应，
它的条目分配、提交等操作和 LSQ 一起进行。TPBuf 包含了所有进行中的推测式
的访存指令。为了避免攻击者访问未授权的数据，并将其传播到自己的地址空间，
需要先用 TLB 获得访问的物理地址，将其写入 TPBuf 中该访问对应的一
项。TPBuf 检测 S-pattern，并将结果再传至缓存命中过滤器，决定是否阻止一
个可疑推测式执行的请求。

% 分配：当在LSQ中分配存储器访问指令时，它们也在TPBuf中分配并且A位被置位。并且根据TPBuf中的A位生成掩码。
% 它指示TPBuf中的哪些内存指令比程序顺序中的新条目旧。

% 更新：使用存储器指令附带的可疑推测标志更新S位。当PPN记录在TPBuf中时，V位置位。当存储器指令获取的数据可供其他指令使用时，W置位。

% 检测：当传入请求进入TPbuf时，TPBuf将其PPN与现有条目的PPN进行比较，然后生成地址匹配向量（匹配）。这些矢量，包括Match，V，W和S，用作等式1逻辑的输入，以确定请求是否安全。特别地，“|”表示减少OR，它对向量中的所有位进行OR运算以生成1位输出。
%%%%%%%

在 gem5 模拟器中用 SPEC CPU2006 进行评测，使用基于缓存命中的过滤器时，
平均性能开销为 12.8\%，再加上 TPBuf 过滤器，平均性能开销可以再降低至
为 6.8\%.

\subsubsection{SpectreGuard}

SpectreGuard\supercite{spectreguard} 是一种以数据为中心的软硬件结合
的Spectre 防御方案。这种方案考虑到对于软件开发人员来说，识别程序中的秘
密数据（如密钥）比识别可能受到攻击的 Spectre 组件代码容易。因
此，SpectreGuard 在操作系统中提供接口，使程序可以标记敏感的内存区域，硬
件上只需要简单的微体系结构扩展，就可以阻止 Spectre 攻击。和已有的防御方案相
比，它不需要依靠编译器识别程序中可能受攻击的代码，也不需要复杂的硬件修
改。

Spectre 攻击需要分为三个步骤：从存储系统装载秘密数据，将秘密数据转发至
依赖于这个数据的指令，执行依赖于这个数据的指令，通过微体系结构隐蔽信道泄露
秘密。数据的泄露在第二、三步进行，如果秘密数据被装载，但不被转发，秘密
也不会泄露给攻击者。

因此，SpectreGuard 允许第一步发生，但将第二步延迟至装载秘密数据的指令
之前所有分支均已解决。并且，在经过软件标记之后，第一步中装载的数据仅当在敏感
内存区域时，才需要延迟转发。当秘密数据不经常访问时，总体开销可以忽略。

敏感数据的标记通过在页表中添加一个位实现，在软件中可以扩展操作系统
的mmap, mprotect 等接口，或通过运行库、编译器、链接器实现。在微体系结构中，
指令执行后，执行结果将转发至后续依赖于这个指令的指令，而如果指令是访问
了敏感区域的指令，则推迟它的转发。

在 gem5 模拟器中用 SPEC CPU2006 对几种标记方案进行评测，方案 SG(All) 标
记内存的所有页，平均性能开销为 20\%, SG(Heap) 只标记堆区域，性能开销减
少至8\%. 在使用 OpenSSL 的合成基准测试中，如果只标记私钥为秘密数据，则
性能可以接近原处理器的性能。

% \begin{comment}
% 
%   MI6: Secure Enclave in a Speculative Out-of-Order processor
% 
%   abs: 一个考虑了Spectre等攻击设计的enclave方案
% 
% 
%   Data Oblivious ISA(OISA)是一种 ISA 扩展，用于阻止信息通过侧信道泄露。
%   设计原则：
%   \begin{enumerate}
%   \item ISA的安全性和微体系结构无关
%   \item ISA不会阻止现代微处理器的优化技术
%   \end{enumerate}
% 
%   OISA有以下组件：
%   \begin{enumerate}
%   \item 动态跟踪敏感数据：使用DIFT跟踪程序运行时私密数据在处理器中的传
%     播。所有数据都有 confidential/public 标记，标签在读取操作数时必须
%     解析
%   \item 指令定义操作数可以接受 public 还是 public/confidential 数据
%   \end{enumerate}
% \end{comment}
% 
% % vim:ts=4:sw=4

\section{Meltdown 和 Spectre 的防御方案分析}

由于 Meltdown 型攻击和 Spectre 型攻击利用的暂态指令不同，因此防御方案
也不同。

对于 Meltdown 型攻击，防御方法相对简单。只要使产生异常的指令在微体系结构层
次上无法访问体系结构层次上无法访问的数据，即可以阻止 Meltdown 型攻击。
在现有的处理器中，需要使用 KAISER 等防御方案，会造成一定性能的损失。

SafeSpec 和 InvisiSpec 也将防御 Meltdown 型攻击作为它们的目标之一，但
是在现有的 Meltdown 型攻击中，泄露数据的隐蔽信道由攻击者构造，攻击者可
以利用缓存之外的信道泄露数据，因此只阻止缓存信道不能有效地防御
Meltdown 型攻击。

Spectre 攻击的防御相对复杂。对于软件防御方案，在分支后添加串行化指令或
数据依赖，阻止推测式执行的发生，会有 30\% 以上的性能下降。
\supercite{spec-load-hardening} 使用静态分析工具
\supercite{oo7}\supercite{spectector}寻找程序中受影响的分支，可以检测
出受 Spectre 攻击影响的分支，并只对这些分支进行防御，但是这些工具仍然
未能投入使用。而且，这些方法都只能防御 Spectre v1. Retpoline 有 5\% 至
10\% 的性能开销\supercite{systematic}，只能用于防御 Spectre v2. 索引掩
码只用于数组边界检查一类分支，站点隔离只用于浏览器等特定场景。

总的来说，软件防御方案防御的攻击类型有限，而且需要对软件进行修改，而硬
件防御方案通常不要求软件修改，可以防御多种变体的攻击，这是硬件防御方案
相对于软件防御方案的优点。

现有的硬件防御方案主要关注高速缓存侧信道。在现有的大部分 Spectre 类型
攻击中，都使用高速缓存侧信道，因此针对高速缓存侧信道的防御方法可以抵抗
大多数的攻击。随着侧信道攻击研究的发展，未来的处理器可能需要考虑更多的
信道。

SafeSpec 的方法是使用临时结构。InvisiSpec 为了处理内存一致性的问题，在
添加一个推测式执行缓冲区之外，还要增加一个验证步骤，使得 InvisiSpec 具
有较大的性能开销。

基于缓存隔离的 DAWG 的主要缺点是需要操作系统的支持。此外，DAWG 不能防
御 NetSpectre 等来源于同一安全域的攻击。

Context-Sensitive Fencing 基于译码阶段的微码自定义，它只适用于使用微码
的处理器。

Conditional Speculation 定义了安全相关的概念，并将其用于安全冒险检测，
这个概念可以用于所有可泄露数据的侧信道。对于缓存侧信道，基于缓存命中的
过滤器和 TPBuf 过滤器结合，性能开销较小。TPBuf 主要用于防御基于共享内
存的侧信道攻击，而不能防御不利用共享内存的缓存侧信道攻击，这是一个主要
缺点。

SpectreGuard 是基于隐私数据标记的防御方法，它针对 Spectre 攻击中所有的
侧信道，而不限于缓存信道。它的设计简单，但是需要软件的支持。

\section{小结}

本章介绍了现有的 Meltdown 和 Spectre 的防御方法，重点介绍 Spectre 的防
御方法，包括软件防御和硬件防御。最后分析了各种防御方案的特性和缺点。
