% Copyright (c) 2014,2016 Casper Ti. Vector
% Public domain.

\chapter{Meltdown 和 Spectre攻击}\label{sec:attack}

本章首先介绍侧信道攻击，之后对已有的 Meltdown 和 Spectre 攻击的各种变
体进行分类讲解，最后简单地介绍已有攻击的其他使用形式。

\section{侧信道攻击}

侧信道攻击是利用计算的过程对环境产生的影响，从而获取秘密数据的技术。
1996年，Paul Kocher 发现通过对私钥的操作进行计时，可以找出
Diffie-Hellman，RSA 等密码的私钥。\supercite{TimingAttack} 这是最早的
侧信道攻击的研究。之后研究者发现了更多侧信道，如功耗
\supercite{MOP2010}、电磁辐射\supercite{EMpower}、温度
\supercite{hutter}、噪声\supercite{acoustic}等。

侧信道攻击的一个用途是用于构造隐蔽信道（covert channel）。隐蔽信道是指
在系统安全策略定义之下，不被允许传输数据的双方，用于传输数据的信道。

现代计算机系统采用多种优化手段，优化可以作用于处理的数据的值、数据的位
置、数据存放位置的访问频率等，攻击者可以通过这些优化产生的效果，推断出
计算机系统处理的秘密数据的值。基于软件的微体系结构侧信道攻击
\supercite{gruss}利用微体系结构优化造成的时间区别，这种攻击不需要物理访问
计算机系统，只需要在系统上运行代码。

高速缓存计时攻击是一种重要的侧信道攻击的方式。当高速缓存中存在一个地址
的数据时，访问这个地址的时间更短，利用这个性质，攻击者可以得知一个地址
是否曾经被访问过。缓存计时攻击的常见形式有：

\begin{itemize}
\item Evict+Time: 攻击者清除一个缓存组，然后对一个算法的运行进行计时，
  检查它的运行时间是否受到影响
\item Prime+Probe: 受害者运行一段程序后，攻击者访问缓存某一组的每一路，
  观察访问时间是否有变化
\item Flush+Reload: 攻击者利用 x86 的 clflush 等指令清除一个共享内存地
  址对应的缓存行，在受害者运行一段程序后，攻击者重新装载该行对应的地址
  的数据，通过测量访问时间推断受害者是否访问了这个共享的地址
\end{itemize}

\section{Meltdown和Spectre攻击}

处理器在执行指令遇到异常，或者推测式执行了错误的指令后，会回卷错误执行
的指令，丢弃它们对体系结构状态的修改，这些被回卷的指令称为暂态指令
（transient
instruction）。Meltdown\supercite{meltdown} 和
Spectre\supercite{spectre} 是最早发现的两个利用暂态指令的攻击。此后发现
了更多的 Meltdown 和 Spectre 的变体。为了找出更多的攻击类型和分析它们的
防御方案，Claudio Canella 等人对这些攻击及其防御做了系统性的研
究\supercite{systematic}，将所有暂态执行攻击分为 Meltdown型攻击
和 Spectre 型攻击两类。

\subsection{Meltdown型攻击}

Meltdown 型攻击利用的是处理器异常产生的暂态指令，这些指令使用了体系结构
层次上不可访问的数据，从而绕过硬件的安全策略，泄露体系结构层次上不可访
问的数据。

\subsubsection{Meltdown}

内存隔离是现代操作系统的核心安全功能之一。操作系统确保用户程序无法访问
其他程序的内存或内核的内存。这种隔离是现代计算环境的基石，使得系统上可
以同时运行多个应用程序，和多个用户的进程。

在现代处理器中，内核和用户进程之间的隔离通常由处理器的定义的权限位实现，
这一位定义了内核的页是否可以被访问。这一位只能在进入内核态时设置，并在
切换到用户态时清除。这个硬件特性使得操作系统可以将内核映射到每个进程的
地址空间，在中断等事件发生时，可以高效地从用户进程切换至内核。因此，在
操作系统的实现中，处理器从用户进程切换到内核时，不会改变内存映射。

在 Meltdown\supercite{meltdown} 攻击中，任何用户进程都可以简单地读取所在机器的整个
内核内存（包括映射在内核区域中的所有物理内存），从而完全破坏内存隔
离。Meltdown 不利用任何软件漏洞，适用于所有主流操作系统。Meltdown 利用了
大多数现代处理器中的侧信道信息，可以用于 2010 年以来的现代 Intel 处理器，
也可能可用于其他厂商的处理器。

侧通道攻击通常需要非常具体地了解目标应用程序，并且只用于泄露其中的秘密
信息，但 Meltdown 允许攻击者在可被攻击的处理器上，通过运行代码获得整个
内核地址空间的数据，包括任何映射在内核中的物理内存。Meltdown 之所以简单和强大，是因为它利用了乱序执行产生的副作用。

乱序执行是现代处理器用于提高性能的重要特性，用于隐藏功能单元的延迟，例
如，读取内存的功能单元需要等待存储器中的数据到达。现代处理器在遇到长延
迟的执行时，不会使处理器停顿，而是可以乱序操作，将后续的操作调度到空闲的功
能单元中。

从安全的角度，有一个发现很重要：易受攻击的乱序处理器允许非特权态的进
程将数据从特权（内核或物理）地址载入临时的寄存器中。进一步，处理器可以
基于该寄存器的值进行计算，例如用这个寄存器的值访问数组。如果一条指令不
应该执行，通过简单地丢弃访问内存得到的结果，处理器可以确保程序执行的正
确性。因此，在体系结构层次，即处理器执行计算的抽象定义下，不会出现安全
问题。

但是，乱序执行中的内存访问会影响缓存，而缓存的状态又可以通过缓存侧通道
进行检测。因此，攻击者可以通过在乱序执行的指令流中，读取特权态的内存来，
来得到整个内核内存的数据，并通过微体系结构隐蔽通道传送出来，然后攻击者在隐
蔽信道的接收端，找出这个临时寄存器的值。因此，在微体系结构层次，即实际的
硬件实现中，存在可利用的安全性问题。

% background: address spaces
为了将不同的进程相互隔离，处理器支持虚拟地址空间，它将虚拟地址翻译为物
理地址。虚拟地址空间将内存划分为一系列的页，系统通过多级页表将虚拟页翻
译为物理页。页表是一个多级的结构，存放虚拟页到物理页的映射，同时，它还
保存了每个页的保护属性，用于处理器进行权限检查，例如可读、可写、可执行、
用户可读等。操作系统中每一个运行中的进程有一个对应的页表，页表所在的位
置存放在处理器中的一个特定寄存器中，在上下文切换时，操作系统会更新这个
寄存器，从而切换到处理器的进程可以使用这个进程对应的页表。利用这个机制，
每个进程的虚拟地址空间映射到不同的物理区域，每个进程只能使用自己地址空
间中的数据，从而达到进程间隔离的效果。

每个地址空间都可以分为用户地址空间和内核地址空间。正在运行的进程可以访
问用户地址空间，而内核地址空间只有进入内核态后可以访问，操作系统通过在
页表中设置内核地址空间的页为用户不可访问，来实现这个特性。内核地址空间
除了包含内核所要使用的数据外，通常还保存了整个物理内存的映射、文件缓存
等内容。

操作系统将内核地址空间映射至每个用户进程的地址空间中。处理器执行访存指
令时，在将虚拟地址翻译为物理地址的时候，检查地址对应的权限位，判断用户
是否可访问该地址。Meltdown攻击中，攻击者让处理器从用户不可访问的内核地
址装载数据至寄存器，由于所有内核地址都可以翻译为合法的物理地址，处理器
可以读取内核地址空间的数据，在产生异常前，攻击者构造的暂态指令序列可以
将读取到的数据通过隐蔽信道发送给攻击者。

图\ref{lst:meltdown}是一段 Meltdown 攻击的示例代码，通过它可以读取内核地址空间的数据：

\begin{figure}
  \centering
\begin{minted}{nasm}
xor rax, rax
retry:
mov al, byte [rcx] ; 读取内核空间的数据
shl rax, 0xc
jz retry
mov rbx, qword [rbx + rax] ; 访问探测数组
\end{minted}
\caption{Meltdown 示例代码}
\label{lst:meltdown}
\end{figure}

这段 X86 指令读取了以 rcx 为地址的内核空间的数据，在用户态下，这个读取
内核空间数据的指令会产生异常，但是在处理器产生异常之前，这条指令可以将
内核空间的数据读至寄存器，并且其后的指令也会执行，访问 rbx 指向的探测数
组。在处理器回卷了产生异常的指令和其后执行的指令后，高速缓存中仍然保留
了探测数组和内核数据相关的元素，从而攻击者可以通过扫描探测数组的每一
项，观察访问时间，以推测内核空间数据的值。

Meltdown 打破了处理器内存隔离功能提供的所有安全保障。这种攻击可以用于现
代台式机、笔记本电脑以及云服务器。 Meltdown 允许非特权进程读取映射在内
核地址空间中的数据，包括 Linux，Android 和 OS X 上的整个物理内存，以
及 Windows 上的大部分物理内存。这些内存可能包含其他进程的物理内存，以及
内核共享的容器沙箱（如 Docker，LXC）或半虚拟化模式下的 Xen 中，虚拟化管
理程序的内存，和其他虚拟化实例的内存。Meltdown 攻击的性能取决于处理器速
度、TLB 和高速缓存大小、DRAM 速度等，可以以 3.2KB/s 到 503KB/s 的速度
读取内核内存。因此，大量系统受到影响。

\subsubsection{Foreshadow}

% foreshadow introduction
由于现代广泛使用的操作系统和应用程序的大小可以轻松地达到数百万行代码，
并且单个漏洞通常会破坏所有安全保证，所以在安全上很难信任现有的操作系统
和应用程序。为了应对这些挑战，学术界和工业界开发了可信执行环境
（Trusted Execution Environment, TEE），其中包含一个处理器常规执行环境
之外的非分层保护模型，用于隔离应用程序，称为 enclave. TEE 利用只含有处
理器和微码的最小可信计算基础（Trusted Computing Base），来保证相互不信
任的 enclave 的保密性和完整性。 每个 enclave 的私有处理器和内存状态只可
以由其内部运行的代码访问，并且在任何特权级别（包括潜在的恶意操作系统和
虚拟机管理程序）上运行的所有其他 enclave 和软件都无法访问。除了强大的内
存隔离外，TEE 通常还提供一个证明（attestation）原语，从而可以本地或远程
地在运行时从密码学上验证特定的 enclave 已经加载到真正的（因而被认为是安
全的）TEE 处理器上。

随着 2013 年 Intel 软件防护扩展（Software Guard eXtention,
SGX）\supercite{sgx}的发布，硬件强制的 TEE 隔离和证明可以在现成的x86处
理器上使用。由于 Intel SGX 承诺的强大安全保障，越来越多的行业参与者将这
项技术应用于可受攻击者控制的机器上，需要安全执行的各种应用程序中。Open
Whisper Systems 依靠 SGX 在 Signal 网络中实现隐私友好的联系人发现功
能。\supercite{signal-sgx} 微软和 IBM 最近都宣布在其云基础架构中支
持 SGX。各种现成的蓝光播放器以及 4K Netflix 客户端使用 SGX 对高分辨率视
频流实施数字版权管理（DRM）。新兴的加密货币\supercite{mobilecoin}和的区
块链\supercite{sawtooth}技术更加依赖于 SGX 的正确性。

然而，当前的 SGX 实现并没有满足它的安全目标。Foreshadow 攻击利用现
代 Intel 处理器中的乱序执行机制，可以从处理器的缓存中泄露明文
的 enclave 数据。Foreshadow 攻击的核心是利用 Meltdown 攻击相同的处理器
漏洞，即处理器的访问控制逻辑允许攻击者在瞬态的乱序执行指令回滚之前，使
用未授权内存访问的结果。然而，Meltdown 攻击针对传统的分层保护域，
而 Foreshadow 考虑了一种非常不同的攻击模型，攻击者的目标不是从用户空间
读取内核内存，而是破坏最先进的地址空间内 enclave 保护域，它不在已经部署
的用于防御 Meltdown 的内核页表隔离技术的保护之内。Foreshadow 使用了一
种新的漏洞利用方法，并且基本的攻击完全可以由无特权的攻击者使用，而无需
用 root 权限访问受害者机器。对于有 root 权限的攻击者，还可以使用一组可
选的内核级优化技术，进一步降低 Foreshadow 攻击的噪声。

Foreshadow 对 Intel SGX 所追求的安全模型产生了深远的影响，在没有微码补
丁的情况下，当前的SGX处理器无法保证 enclave 的数据保密性，也无法证
明enclave 执行的完整性，包括英特尔自己的 enclave 架构。此外，尽管 SGX
希望内核级别的攻击者，但现有的 SGX 处理器甚至无法在没有特权的用户空间
攻击者面前保护 enclave 中的秘密数据。

所有先前已知的针对 SGX 的攻击都依赖于软件特定的侧信道泄露或软件漏
洞。 人们普遍认为，精心编写的 enclave 可以通过坚持良好的编码实践，例如
不使用依赖秘密数据的分支，来防止信息泄露。Intel 认为这些攻击都没有破
坏 SGX 的安全保证，防止侧信道攻击应该是 enclave 开发者的责任。 然
而，Foreshadow 否定了这一论点，因为它完全依赖于基本的 Intel x86 处理器
的行为，并且不利用任何软件漏洞，甚至不需要知道受害者 enclave 的源代码。

% foreshadow attack
\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{addr-trans.eps}
  \caption{地址翻译过程和 L1 终端错误的原理\supercite{foreshadowNG}}
  \label{fig:addr-tran}
\end{figure}

在现代 Intel x86 处理器中，用一个虚拟内存地址访问内存时，处理器先在TLB
中查询对应的物理地址，如果 TLB 中没有这个地址对应的项，则会在页表中找到
虚拟地址对应的页表项，以获得对应的物理地址和访问权限信息。
如果在虚拟机环境中，则需要如图 \ref{fig:addr-tran} 所示，增加一个扩展
页表（Extended Page Table，EPT）的访问过程，将客户机的物理地址翻译为底
层物理机的物理地址。最后，启用了 Intel SGX 的处理器还会进一步检查地址
翻译的结果，确保这个地址翻译遵守硬件强制的 enclave 访问控制限制。如果
这三个独立的阶段报告一个访问违例，则处理器抛出一个页错误，控制流转向异
常处理代码，而对于 SGX，大多数的 enclave 内存违例会被处理器忽视，将内
存装载指令得到的值设为 abort page 的值 -1.

在体系结构层次上，以上的地址翻译过程在文档中有精确的描述。现代处理器为
了加快地址翻译和内存访问，使用了并行访问和额外的缓存结构。在 Meltdown类
型的攻击中，可以发现 Intel 的 CPU 将内存访问违例检查放到了指令提交的时
间，使得在页错误发生之前，未授权的内存内容仍然可以到达流水线中其后执行
的乱序执行的指令。Meltdown 攻击利用了这个时间窗口，在暂态指令中，将只有
特权态能访问的内存编码至持久的微体系结构状态。

现代 Intel 处理器使用虚拟地址索引，物理地址标签的一级缓存。如
图\ref{fig:addr-tran} 所示，这种缓存索引方式使得地址翻译可以和一级缓存
的查询并行。在得到虚拟地址对应的一级缓存组后，处理器还需要知道是否有某
一路缓存含有需要的数据，因此处理器要将地址翻译得到的物理地址和缓存每一
路的物理地址标签对比，在某一个有效的缓存路存在正确的物理地址时，此时一
级缓存命中，数据返回至处理器的执行单元。

Foreshadow\supercite{foreshadow} 是一种可以读取 Intel SGX enclave 中秘
密数据的 Meltdown 型攻击。如果对 SGX 使用 Meltdown 攻击，攻击者读取未授
权的 enclave 内存，不会产生异常，而是读出 abort page 值 -1. 但是 abort
page 只在页权限检查通过后发生，攻击者可以将该页 present 位清除，即将该
页设为不存在，此时处理器便会产生异常，但由于一级缓存使用虚拟地址索引，
因此仍然可以从一级缓存读取该虚拟地址中的数据，从而暂态指令可以泄露 SGX
enclave 中的数据。

通过将一个页设为不存在，绕过地址翻译，从一级缓存读取数据的攻击方
式，Intel 将其称为L1终端错误（L1 Terminal
Fault）\supercite{l1tf}。Foreshadow-NG\supercite{foreshadowNG} 利用相同
的原理，新增了Foreshadow-OS 和 Foreshadow-VMM 两种 Foreshadow 类型的攻
击，可以绕过操作系统和虚拟机的隔离。


% forshadow-OS
操作系统内核将虚拟内存页从内存交换至外部持久存储时，都需要清除页表项的
存在位。但是，根据处理器的体系结构规范\supercite{intel-spec}，内核可以
根据需要，自由地使用标记为不存在的页表项中的剩余位。例如，操作系统可以
保持这些位不变，将它们归零，或者使用它们来存储用于把页从磁盘复制回内存
的元数据。因此，虽然非特权用户空间应用程序不能直接控制页表项，但是当页
表项取消映射相应的虚拟页时，操作系统在页表项中留下的元数据仍然可以指向
包含敏感数据的有效物理地址。在内核清除相应页表项中的存在位后，从用户空
间解引用未映射的页将导致终端错误。

Foreshadow-OS 利用了这种错误造成的暂态执行的指令。虽然访问页表中标记为
不存在的页会触发终端错误存在，在页表项中的元数据表示在缓存中的物理地址
的情况下，处理器仍然将一级数据高速缓存中未授权数据传递到暂态乱序执行中。
然后可以和其他暂态执行攻击一样，将秘密数据从微体系结构暂态执行域带入体系结
构状态中，例如，使用基于缓存的隐蔽通道。更糟糕的是，如果操作系统支持大
于 4KB 的页（例如，2MB 或 1GB），攻击者可以通过无意的映射访问最大页面大
小的内存范围。由于所有软件本质上共享相同的物理地址空间，因此元数据创建
的虚拟到物理映射可能指向属于操作系统内核，VMM 内存，SGX
enclave，或 SMM 内存的数据。在操作系统通过 munmap 系统调用释放内存时，
将页表项清零的常见情况下，攻击者可以访问存储在物理地址 0x00 的数据。

实验结果表明，在一个 i7-6820HQ CPU 上，Foreshadow-OS 通过利用瞬态执行，
可以从已清除存在位的页中读取一级缓存的数据。

% foreshadow-VMM

虽然上述 Foreshadow-OS 变体允许无特权的攻击者使用未经授权的物理内存位置
上的数据进行暂态计算，但他们无法直接控制访问哪些物理地址。因此，
Foreshadow 攻击使用在可以直接控制客户机器物理地址和一级缓存标签比较的
不可信虚拟机时时，会变得更有破坏性。这种攻击称为 Foreshadow-VMM.

根据 Intel 处理器的架构规范\supercite{intel-spec}，客户机物理地址经过基
于 EPT 的转换过程以找到底层主机物理内存地址。英特尔的分
析\supercite{l1tf}显示，终端错误发生时，虚拟地址到物理地址处于早期阶段，
在经过 EPT 转换之前，将转换出来的客户机物理地址传到一级缓存中进行标签比
较。因此，恶意虚拟机可以控制用于访问一级缓存的暂态指令的物理地址。特别
的，不可信的恶意客户虚拟机可以在自己的客户页表中修改页表项，指向客户机
物理内存。该地址不会经过 EPT 转换，和一级缓存中的标签进行比较，就像它是
主机物理地址一样。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{foreshadow-vmm.eps}
  \caption{Foreshadow VMM 的一种使用场景\supercite{foreshadowNG}}
  \label{fig:foreshadow-vmm}
\end{figure}

图 \ref{fig:foreshadow-vmm} 展示了一种情况，一个恶意的虚拟机可以利用瞬
时执行攻击窃取属于同一处理器核的另一个虚拟机中的数据。这种攻击并不局限
于超线程受害者，因为 Foreshadow-VMM 可以提取处理器物理核心一级数据高速
缓存中的任何秘密。


\subsubsection{LazyFP}

% introduction
LazyFP\supercite{lazyfp} 是一个 Meltdown 类型的攻击，它可以跨越进程和虚
拟机的边界，泄露浮点处理单元（FPU）寄存器的状态。这种攻击利用 Intel 处
理器处理设备不可用异常（\Fault{NM}）的行为。这个异常用于在操作系统中实
现延迟 FPU 上下文切换的优化。在 LazyFP 攻击中，没有特权的本地执行的代码，
可以利用 \Fault{NM} 异常的暂态指令，恢复受害者进程的 FPU 寄存器状态。
由于 AES-NI 的密钥等数据可能存在 FPU 寄存器中，这种攻击导致的数据泄露
对信息安全是个很大的威胁。

% background

x87 浮点单元（FPU）是 x86 的处理器的一个扩展，其最初目的是加速浮点数的
运算。它有自己的一组指令和寄存器。自 1989 年推出 Intel 486DX 以来，它是
每个 x86 微处理器不可或缺的一部分，而在此之前，它是一个可选的外部协处理
器。在上下文切换中，向内存保存 FPU 状态和恢复 FPU 的状态曾经是一个成本
很高的操作，因为内存速度慢并且大小有限。此外，那时 FPU 只在少数程序中被
使用。在 FPU 不被所有进程使用的情况下，在每个上下文切换时，切换 FPU 状
态会产生不必要的开销。为了能够减少这种开销，x86 处理器引入了一个控制寄
存器位 cr0.ts，允许操作系统打开和关闭FPU，当FPU关闭时，它会保持其状态，
但用户和内核代码都无法访问。

利用 cr0.ts 实现的优化称为延迟 FPU 上下文切换（lazy FPU context
switching）。核心思想是 FPU 寄存器状态仅在必要时进行上下文切换，并保留
在不使用 FPU 的进程中。这样，操作系统可以避免不必要的保存和恢复 FPU 上
下文的成本。

操作系统跟踪当前 FPU 寄存器状态所属的进程，此进程称为FPU所有者。FPU 可
以在启用和禁用状态，在 FPU 启用状态下，当前进程是 FPU 所有者，可以自由
地执行 FPU 指令。当操作系统切换到另一个进程时，FPU 被禁用。在进程尝试执
行 FPU 指令之前，它将保持禁用状态。由于 FPU 被禁用，使用浮点指令时，处
理器会生成 \Fault{NM} 异常。操作系统处理 \Fault{NM} 异常的方式取决于当
前进程是否为FPU 所有者，如果是，则 FPU 寄存器状态属于该进程，操作系统启
用FPU，该进程再次可以自由执行FPU指令。否则，则操作系统在启用 FPU 的同时，
将当前FPU 寄存器状态存储到 FPU 所有者进程的状态保存区域，并恢复当前进程
的FPU 寄存器状态，并将该进程设为 FPU 的所有者。

这个算法在不同操作系统中的实现可能不同，它们的共同点是，使用 FPU 的进程
切换到不使用 FPU 的进程时，操作系统不需要上下文切换 FPU 寄存器的状态。

虽然延迟 FPU 上下文切换仍然在现代的操作系统中广泛使用，但其基本假设通常
不再适用。从 Intel MMX 指令集扩展，到后续的 SSE，AVX 和 AVX-512，FPU 寄
存器状态已经扩展到更大的 SIMD 寄存器。SSE 指令集是 64 位 x86 处理器的必
须组成部分，并且在几乎所有程序和库中用于实现不同的任务，例如内存复制。

虽然更大的 FPU 寄存器状态使得避免 FPU 上下文切换更具吸引力，但在当前的
软件中，每个进程最终都会使用 FPU 寄存器并触发 \Fault{NM} 异常。因
此，Linux 内核已经默认将 FPU 寄存器作为普通上下文切换的一部分进行切换，
在正常操作中不会生成 \Fault{NM} 异常。

在使用 Intel 的硬件辅助虚拟化技术时，处理器在从客户机切换到管理程序时，
不会切换FPU寄存器状态。虚拟机管理程序负责上下文切换 FPU 寄存器的状态，
并且机制与非虚拟化操作系统的机制相同。因此，延迟 FPU 上下文切换也适用于
虚拟机管理程序。

在使用延迟 FPU 上下文切换的操作系统中，利用 LazyFP 攻击可以使没有特权的
攻击者获取同一处理器硬件线程中 FPU 寄存器的数据。以下简单的例子可以泄
露1比特的数据。

\begin{figure}
  \centering
\begin{minted}{nasm}
movq rax, xmm0
and rax, 1
shl rax, 6
mov rax, dword [mem + rax]
\end{minted}
\caption{LazyFP 示例代码}
\label{lst:lazyfp}
\end{figure}

在以上代码片段中，由于进程中 FPU 寄存器被禁用，movq 指令产
生 \Fault{NM} 异常。操作系统会用对应用程序透明的方式处理该异常，恢复进
程的状态并继续执行。

而在处理器结束 movq 指令的执行并产生 \Fault{NM} 之前，已经执行了后续指
令，虽然最终这些指令引起的体系结构状态更改会被丢弃，但在缓存中的状态仍
然保留。这段代码使用了受害者进程的 FPU 寄存器和攻击者进程的通用寄存器
进行暂态指令的执行，通过探测 mem 指向的数组的缓存状态，可以恢复 xmm0
最低位的值。

利用 Intel TSX 等技术处理异常，攻击者可以高效地泄露所有 FPU 寄存器的数
据。在 Intel Core i7-5600U 处理器中，可以以 3.3MB/s 的速度泄露一个 AVX
寄存器的数据。

\subsection{Spectre 型攻击}

Spectre 型攻击利用处理器对控制流或数据流的预测，并进行推测式执行产生的
暂态指令。Spectre 型攻击中，暂态指令访问的是程序在体系结构层面能访问的
数据，可以绕过软件定义的安全策略，如边界检查等。

推测式执行的常见形式是控制流的推测式执行。控制流的推测式执行依赖于转移
预测技术，包含了分支的预测、间接转移目标地址的预测和函数返回地址的预测，
对应于 Spectre 攻击中 Spectre-PHT, Spectre-BTB, Spectre-RSB. 此外，处
理器预测装载指令和程序序列之前访问地址未知的存储指令地址不冲突，
推测式执行这样的装载指令，它对应 Spectre-STL.

%%%% gtran: branch prediction

%% \Todo: 关于处理器执行转移指令的背景知识，是否需要？

%% \Fixme: 需要重新翻译的内容

%% 在推测执行期间，处理器猜测分支指令的可能结果。更好的预测通过增加可成功
%% 提交的推测性执行操作的数量来提高性能。

%% 现代英特尔处理器的分支预测器，例如Haswell Xeon处理器，具有用于直接和间
%% 接分支的多种预测机制。间接分支指令可以跳转到在运行时计算的任意目标地址。
%% 例如，x86指令可以跳转到寄存器，存储器位置或堆栈中的地址，例如“jmp eax”，
%% “jmp [eax]”和“ret”。 ARM上也支持间接分支（例如，“MOV pc，r14”），MIPS
%% （例如，“jr \$ ra”），RISC-V（例如，“jalr x0，x1,0”）和其他处理器。与直
%% 接分支相比，为了补偿额外的灵活性，使用至少两种不同的预测机制来优化间接
%% 跳转和调用[35]。

%% 英特尔[35]描述了处理器的预测
%% •以静态或单调方式进行“直接呼叫和跳转”，
%% •“间接呼叫和跳转”要么是单调的，要么是以不同的方式，这取决于最近的程序
%% 行为，以及
%% •“条件分支”分支目标以及是否将采用分支。

%% 因此，几个处理器组件用于预测分支的结果。分支目标缓冲区（BTB）保持从最
%% 近执行的分支指令的地址到目标地址的映射[44]。即使在解码分支指令之前，处
%% 理器也可以使用BTB来预测未来的代码地址。 Evtyushkin等。 [14]分析了英特
%% 尔Haswell处理器的BTB，并得出结论，只有31个最低有效位的分支地址用于索引
%% BTB。

%% 对于条件分支，记录目标地址对于预测分支的结果不是必需的，因为目的地通常
%% 在指令中被编码，同时在运行时确定条件。为了改进预测，处理器维护分支结果
%% 的记录，包括最近的直接和间接分支。 Bhattacharya等。 [9]分析了近期英特
%% 尔处理器中分支历史预测的结构。

%% 尽管返回指令是一种间接分支，但是在现代CPU中经常使用用于预测目标地址的
%% 单独机制。返回堆栈缓冲区（RSB）维护最近使用的调用堆栈部分的副本[15]。
%% 如果RSB中没有可用数据，则不同的处理器将停止执行或使用BTB作为回退[15]。

%% 分支预测逻辑（例如，BTB和RSB）通常不在物理核心之间共享[19]。因此，处理
%% 器仅从在同一核上执行的先前分支学习。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Spectre 攻击使得受害者程序在推测式地执行在严格按序执行的时不会执行的操
作，这些操作会通过隐蔽信道将秘密信息泄露给攻击者。

Spectre 攻击通常从设置阶段（setup phase）开始，攻击者执行一些用于训练
处理器的操作，使得处理器在后续阶段执行可被攻击者利用的错误的推测式执行。
此外，设置阶段中，攻击者可以操作高速缓存状态，从缓存中去除预测的分支所
依赖的数据，使得推测式执行中的指令足够多，从而可以执行攻击者希望处理器
执行的指令。在这个阶段，攻击者还可以进行隐蔽信道的准备，如
Flush+Reload 攻击中清除探测数组对应缓存行的操作。

在第二个阶段，处理器在推测式执行中，将受害者上下文中的秘密信息，传送至
微体系结构隐蔽信道。攻击者向受害者通过系统调用等方式发送请求，以出发这个推
测式执行。攻击者也可以通过自身代码的推测式执行，从同一进程获取敏感信息，
例如在利用 JIT 编译器的环境中，攻击者可以泄露这个环境中不希望其中执行
的代码访问的数据。

最后一个阶段是恢复敏感数据。对于使用 Flush+Reload 的 Spectre 攻击，恢
复过程的工作之一是对此前清除的缓存行对应的内存地址进行访问计时，从而推
断出敏感数据。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsubsection{Spectre-PHT}

Spectre-PHT 利用分支预测器，攻击者可以训练转移预测器，使得转移预测器执
行一个分支，该分支的指令序列可以泄露受害者地址空间或寄存器的信息。

Spectre v1 是最早提出的 Spectre 攻击，属于 Spectre-PHT 类型。它利用受
害者程序中的分支，一个例子如下：

\begin{figure}[htbp]
\centering
\begin{minted}[frame=single,linenos=true]{C}
if (x < array1_size)
    y = array2[array1[x] * 4096];
\end{minted}
\caption{Spectre v1 受害者代码}
\label{lst:spectre_v1}
\end{figure}

%%%%%%%%%%%%%%%%%%%%%%% gtran: spectre v1 %%%%%%%%%%%%%%%%%%%%%%%%%%%

% \verb|TODO|: Spectre 论文中 Spectre v1 的内容

% \verb|TODO|: 解释 Spectre 攻击中这段代码的运行方式，关于在 Javascript 和 eBPF 下的攻击

% \Fixme: 重新翻译

这段代码 x 的值做边界检查，确保后续执行代码的安全性，同时该检查也防止
处理器读取 array1 数组外的敏感内容。否则，越界的数组下标 x 可能触发异
常，也可以通过构造 x 用于访问敏感数据。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.4\textwidth]{spectre_v1.eps}
  \caption{边界检查结果和分支预测方向的可能情形\supercite{spectre}}
  \label{fig:spectre_v1}
\end{figure}

图\ref{fig:spectre_v1}是边界检查结果和预测的方向的四种组合。在已知边
界检查的结果之前，处理器预测最可能的结果，推测式地执行边界检查之后的代
码。导致边界检查的结果不能及时得出的原因很多，如边界检查期间发生缓存缺
失，缺少空闲的执行单元，存在复杂的数据依赖。在这些情况下，正确预测的可
以使执行时间更短。

但是，处理器可以在不正确的路径推测式执行指令。在这个例子中，假设攻击者
可以控制以如下方式运行：

\begin{itemize}
\item 选择一个恶意越界的 \verb|x| 的值，使得 \verb|array1[x]| 指向受害
  者内存中的某个秘密字节 \verb|k|
\item \verb|array1_size| 和 \verb|array2| 不在缓存中，但 \verb|k| 在缓
  存中
\item 此前的操作中接收到的 \verb|x| 值均有效，使得分支预测器预测 if 为
  真
\end{itemize}

这样的缓存条件可以由攻击者构造，例如，通过构造访问模式造成
\verb|array1_size| 和 \verb|array2| 被驱逐出缓存，再使受害者在合法的操
作中使用秘密 \verb|k|.

在这种条件下，这段代码运行时，处理器比较恶意的 \verb|x| 和
\verb|array1_size|。读取 \verb|array1_size| 导致高速缓存缺失，从而产生
很大的延迟，需要处理器从主存中获得这个值，才可以产生分支结果。同时，分
支预测器预测 if 中的条件为真，因此在推测式执行中，处理器访问
\verb|array1[x]|，此时缓存命中，获得秘密 \verb|k| 的值，然后用它计算
\verb|array2[k*4096]| 的地址，再从存储系统发出读取这个地址的请求。当分
支结果在此之后确定时，处理器从错误的推测式执行恢复。但是推测式执行中读
取 \verb|array2| 的行为影响了缓存状态，而且读取的地址依赖于秘密
\verb|k|.

攻击者通过测量 \verb|array2| 中的哪个位置在缓存中，可以推断出 \verb|k|
的值，因为受害者在推测式执行中将 \verb|array2[k*4096]| 带入了缓存。

这种攻击变体可以用在其他场景下，例如分支的条件可以是类型检查，推测式执
行的代码可以将一个比较结果泄露到一个固定的地址。上述缓存状态在限制更多
的情形，如 \verb|array1_size| 在缓存中时，攻击也可能可以进行。

这种攻击已经在 Intel, AMD 的多个处理器微体系结构，和一些支持推测式执行的
ARM 处理器上进行验证，确认它们受 Spectre v1 的影响。

除了使用原生的机器指令，利用 JavaScript 和 Linux 内核的 eBPF 的 JIT 编
译器也可以进行 Spectre 攻击。

%% 我们在JavaScript中开发了一个概念验证，并在Google Chrome版本62.0.3202中
%% 对其进行了测试，该版本允许网站从其运行的进程中读取私有内存。代码如清单
%% 2所示。

%% 在分支预测器错误引用过程中，索引（通过位操作）设置为范围内值。在最后一
%% 次迭代中，index被设置为simpleByteArray的越界地址。我们使用变量
%% localJunk来确保不优化操作。根据ECMAScript 5.1 Section 11.10 [13]，“|
%% 0”操作将值转换为32位整数，作为JavaScript解释器的优化提示。与其他优化的
%% JavaScript引擎一样，V8执行即时编译以将JavaScript转换为机器语言。虚拟操
%% 作放在清单2中的代码中，以使simpleByteArray.length存储在本地内存中，以
%% 便在攻击期间将其从缓存中删除。有关D8的结果反汇编输出，请参见清单3。

%% 由于无法从JavaScript访问clflush指令，我们使用缓存逐出[27,51]，即，我们
%% 以某种方式访问​​其他存储器位置，以便之后逐出目标存储器位置。泄漏的结果通
%% 过probeTable [n * 4096]的缓存状态传递，n∈0..255，因此攻击者必须驱逐这
%% 256个缓存行。长度参数（JavaScript代码中的simpleByteArray.length和反汇
%% 编中的[ebp-0xe0]）也需要逐出。 JavaScript不提供对rdtscp指令的访问，并
%% 且Chrome故意降低其高分辨率计时器的准确性以使用performance.now（）[62]
%% 来阻止定时攻击。但是，HTML5的Web Workers功能使创建一个单独的线程变得简
%% 单，该线程反复递减共享内存位置中的值[24,60]。这种方法产生了一个提供足
%% 够分辨率的高分辨率计时器。

%% 作为利用条件分支的第三个例子，我们开发了一个可靠的概念验证，它通过滥用
%% eBPF（扩展BPF）接口从未修改的Linux内核泄漏内核内存，而没有针对Specter
%% 的补丁。 eBPF是一个基于伯克利数据包过滤器（BPF）[49]的Linux内核接口，
%% 可用于各种目的，包括根据数据包内容过滤数据包。 eBPF允许非特权用户在内
%% 核的上下文中触发解释或JIT编译以及随后执行用户提供的，内核验证的eBPF字
%% 节码。攻击的基本概念类似于针对JavaScript的攻击概念。

%% 在此次攻击中，我们仅将eBPF代码用于推测性执行的代码。我们在用户空间中使
%% 用本机代码来获取隐藏的信道信息。这与上面的JavaScript示例不同，后者的两
%% 个函数都是用脚本语言实现的。为了推测性地访问用户空间内存中依赖于机密的
%% 位置，我们对内核内存中的数组执行推测性的越界内存访问，其索引足够大，以
%% 便访问用户空间内存。概念验证假定目标处理器不支持超级用户模式访问保护
%% （SMAP）。但是，没有这种假设的攻击也是可能的。它在Intel Xeon Haswell
%% E5-1650 v3上进行了测试，它在默认解释模式和eBPF的非默认JIT编译模式下都
%% 可以使用。在高度优化的实现中，我们能够在此设置中泄漏高达2000B / s。它
%% 还在AMD PRO A8-9600 R7处理器上进行了测试，它只能在非默认的JIT编译模式
%% 下工作。我们将调查原因留给未来的工作。

%% eBPF子系统管理存储在内核内存中的数据结构。用户可以请求创建这些数据结构，
%% 然后可以从eBPF字节码访问这些数据结构。为了强制执行这些操作的内存安全性，
%% 内核存储与每个此类数据结构相关联的一些元数据，并对此元数据执行检查。特
%% 别地，元数据包括数据结构的大小（在创建数据结构时设置一次并用于防止越界
%% 访问）以及加载到内核中的eBPF程序的引用数量。引用计数跟踪引用数据结构的
%% 多少eBPF程序正在运行，确保不释放属于数据结构的内存
%% 加载的eBPF程序引用它。

%% 我们通过滥用错误共享来增加边界检查的延迟与eBPF管理的阵列的长度。内核将
%% 数组长度和引用计数存储在同一缓存行中，允许攻击者将包含数组长度的缓存行
%% 移动到处于Modified状态的另一个物理CPU核心（参见[16,53]）。这是通过加载
%% 和丢弃引用另一个物理内核上的eBPF阵列的eBPF程序来完成的，这会导致内核在
%% 另一个物理内核上递增和递减阵列的引用计数器。这种攻击在Haswell CPU上实
%% 现了大约5000B / s的泄漏率。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% \verb|TODO|: Spectre v1.1的内容，需要更详细地解释攻击原理

推测式缓冲区溢出（Speculative Buffer Overflow）
\supercite{spec-buffer-overflow} 是 Spectre-PHT 的另一种形式。处理器存
在一个存储缓冲区（store buffer），往存储系统写入的数据在指令提交前将写
入数据的地址和值存放在这个缓冲区中，后续的指令要使用这个地址的值时，可
以将缓冲区中的数据转发给需要的指令。推测式缓冲区溢出利用了这个特性，例
如图\ref{lst:spec-buf-overflow}中的程序：

\begin{figure}
  \centering
  \caption{推测式缓冲区溢出}
  \label{lst:spec-buf-overflow}
\begin{minted}{C}
if (x < len)
    a[x] = z;
\end{minted}
\end{figure}

攻击者给出一个超过数组 \verb|a| 大小的 \verb|x| 时，在推测式执行时，会
将 \verb|a[x]| 的地址和攻击者输入的值 \verb|z| 写入至存储缓冲区。如果
\verb|a[x]| 的地址是一个函数在栈中存放的返回地址，这段程序之后有返回指
令，返回指令就会从存储缓冲区读取这个函数的返回地址，并从这个地址开始执
行指令，从而攻击者可以使程序在推测式执行时执行以 \verb|z| 为地址开始的
指令流。通过构造输入 \verb|x| 和 \verb|z|，攻击者可以使处理器在推测式
执行中执行攻击者指定的代码，这些代码可能会将程序中的秘密数据泄露在微架
构状态中。

\subsubsection{Spectre-BTB}

Spectre v2 利用间接转移的推测式执行，用于预测间接转移的主要部件是 BTB，
因此在分类上称为 Spectre-BTB.

在 Spectre v2 中，攻击者可以对间接转移进行投毒，从而可以利用由此产生的
转移预测错误，从另一个上下文（如另一进程）中读取任意内存。如果由于缓存
缺失等原因，需要较长延迟得到间接转移的目标地址，则处理器从此前预测的目
标地址处进行推测式执行。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.4\textwidth]{spectre_v2.eps}
  \caption{转移预测器在不同上下文中训练和使用\supercite{spectre}}
  \label{fig:spectre_v2}
\end{figure}

攻击者可以用恶意的目标地址训练转移预测器，从而处理器转移到攻击者选择的
地址进行推测式执行。在图\ref{fig:spectre_v2}中，转移预测器可以在上下文
中 A 中训练，在上下文 B 中，转移预测器根据在 A 中的训练结果进行预测，
导致处理器跳转至攻击者选择的目标地址处进行推测式执行。攻击者可以引导受
害者推测式执行在正确的程序执行中永远不会执行的位置。由于推测性执行带来
了可测量的副作用，这种攻击对于攻击者来说及其强大，即使在没有可利用的条
件分支错误预测的情况下，也可以用它泄露受害者的内存。

举一个简单的攻击例子，攻击者试图读取受害者的内存，当发生间接转移时，攻
击者可以控制两个寄存器。这在现实的二进制程序中很常见，因为操作从外部接
收数据的函数通常会进行函数调用，此时寄存器包含攻击者控制的值。

攻击者还需要找到一个 Spectre 组件，它是一个用于在推测式执行中，将受害者
的敏感信息转移到隐蔽通道中的代码片段。在这个例子中，一个有效的组件可以
由两个指令组成。一个将攻击者控制的 R1 指向的内存加到攻击者控制的寄存
器R2 上，接下来一个指令用 R2 访问内存。在这种情况下，Spectre 组件可以让
攻击者通过 R1 控制泄露的地址，以及攻击者通过 R2 控制第二条指令读取泄露
数据对应的地址。Spectre 组件需要在受害者可执行的内存中，由于大多数进程
在内存中映射了很大的共享库，攻击者有足够多的空间找到这样的组件，而不需
要搜索受害者程序的代码。

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

%% \verb|TODO|: Spectre 的论文中关于 Spectre v2 一节的剩余内容，一直到实验结果部
%% 分

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

%% 许多其他攻击是可能的，这取决于对手所知道或控制的状态，对手所寻求的信息
%% 所在的位置（例如，寄存器，堆栈，内存等），对手控制推测执行的能力，指令
%% 序列可用于形成小工具，以及哪些渠道可能会泄漏来自投机操作的信息。例如，
%% 如果攻击者可以简单地在将来自寄存器中指定的地址的存储器带入高速缓存的指
%% 令中诱导推测执行，则在寄存器中返回秘密值的加密函数可能变得可利用。同样，
%% 尽管上面的示例假设攻击者控制两个寄存器，但攻击者控制单个寄存器，堆栈上
%% 的值或内存值对于某些小工具来说已足够。

Spectre v2 的攻击方式和面向返回编程（ROP）\supercite{ret2libc} 有相似
之处。在ROP 中，攻击者从受害程序所用的 C 库等代码中寻找大量的指令序列，
合并到一起形成一个组件，攻击者可以构造出能进行任意计算的组件。而在
Spectre v2 攻击中，受攻击的程序是正确编写的软件，构造出的组件是暂态执
行的，指令数目有限，然而攻击者仍然可以让处理器推测式执行复杂的指令序列，
利用侧信道泄露数据。

对处理器的分支预测器进行错误的训练是攻击的一个关键步骤。为此攻击者需要
通过逆向分析出 BTB 的工作方式，用于制造地址冲突。此前已有工作分析了
Intel 处理器的 BTB.\supercite{jump-over-aslr}

%% 请注意，历史错误要求因CPU而异。例如，在Haswell i7-4650U上，使用了大约
%% 29个先前目标地址的低20位，尽管观察到在这些地址上进行了一些进一步的散列。
%% 在AMD Ryzen上，仅使用大约前9个分支的低12位。附录A中提供了用于更新Intel
%% Xeon Haswell E5-1650 v3上的分支历史缓冲区的反向工程伪代码。

%% 此外，我们在攻击者的同一个虚拟地址处设置了一个跳跃，就像在受害者进程中
%% 一样。注意，这可能不是必需的，例如，如果CPU仅基于跳转地址的低位索引预
%% 测。在设计分支预测器时，我们只需要模拟虚拟地址;物理地址，时间和进程ID
%% 似乎并不重要。由于分支预测不受其他内核上的操作的影响（参见第II-C节），
%% 因此必须在同一CPU内核上进行任何错误训练。

%% 我们还观察到分支预测器从非法目的地的跳跃中学习。虽然在攻击者的进程中触
%% 发了异常，但这可以很容易地捕获，例如，使用Linux上的信号处理程序或
%% Windows上的结构化异常处理。与前一种情况一样，分支预测器然后将进行预测，
%% 将其他进程发送到相同的目标地址，但是在受害者的虚拟地址空间（即，小工具
%% 所在的地址空间）中。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\subsubsection{Spectre-RSB}

返回栈（RSB）用于预测程序函数调用的返回地址。RSB 是一个栈结构，当函数调用指令执行时，处理器将返回地址压入 RSB，用于该函数调用对应的返回指令执行时，预测返回的目标地址。当函数返回时，处理器从 RSB 的栈顶得到一个地址，作为预测的返回地址，处理器从这个地址开始推测式执行。当处理器从体系结构的软件栈中得到返回地址，该地址和预测的返回地址相同时，推测式执行正确，否则推测式执行错误。

Spectre-RSB\supercite{spectre-returns}\supercite{ret2spec} 利用了 RSB
预测返回地址错误，导致错误的推测式执行。RSB 预测返回地址错误有四种原因。

第一个原因是由于 RSB 大小有限，会产生上溢和下溢的行为。RSB 的大小通常
和程序调用栈的大小相匹配。调用深度超过 RSB 的大小时，会造成RSB 溢出，
覆盖其中最老的条目，在函数返回时，会发生 RSB 下溢出。在 RSB 下溢出时，
不同处理器用不同方式处理 RSB 为空的情形，如 Intel 的处理器使用 BTB 进
行转移预测，攻击者可以用 Spectre-BTB 的攻击方式进行攻击。

第二个原因是直接污染 RSB，在这种场景下，攻击者可以替换或删除软件调用栈
中的值，从而 RSB 中的值和软件栈中的值不同，导致错误的推测式执行。

第三种方法是推测式污染 RSB. 在一个函数调用指令推测式执行时，返回地址进
入软件栈和 RSB，当推测式执行错误时，虽然调用指令被撤销，但返回地址仍然
在 RSB 中。

第四种方法是跨上下文使用 RSB. 因为上下文切换时，正在执行线程的 RSB 会
被新的线程使用，如果新的线程执行返回指令，则会从原线程提供的 RSB 中的
地址处进行推测式执行。

\subsubsection{Spectre-STL}

Spectre v4 利用的是装载指令的推测式执行。处理器执行装载指令时，如
果此前有地址未知的存储指令，会认为这个装载指令和这些地址未知的
存储指令地址不同，并推测式执行这个装载指令，将其地址中的值装入目的
寄存器。如果在计算出存储的地址后，发现和已执行装载访问了相同数据，
则处理器需要重新执行这个装载和之后的指令。在推测式执行的过程中，暂态
指令可以泄露这个地址中的旧值。

\section{Meltdown 和 Spectre 的其他形式}

在研究 Meltdown 和 Spectre 攻击的过程中，研究者还发现了一起其他的攻击
形式，它们基于已有的 Meltdown 和 Spectre 攻击，使用不同的利用方式。

\subsection{NetSpectre}

NetSpectre\supercite{netspectre} 是通过网络进行的 Spectre 攻击，使得
Spectre 攻击可以用于不运行攻击者控制的代码的系统上，从而更多的设备受到
Spectre 攻击的影响。

和本地 Spectre 攻击类似，远程的 Spectre 攻击需要受害者存在 Spectre 组
件代码，当这些代码可以通过网络接口调用时，攻击者可以向受害者发送一系列
精心构造的请求，通过测量相应时间，泄露受害者内存中的秘密数据。

访问存储器的延迟可以反映在网络请求的延迟中，因此攻击者可以通过大量的测
量，远程地区分缓存是否命中。因此，通过使用 Thrash+Reload，一种基于
Evict+Reload 的远程缓存攻击方式，将 Spectre 攻击用于网络场景，可以每小
时从目标系统泄露 15 比特数据。通过使用基于 AVX2 指令执行时间的侧信道，
可以在 NetSpectre 中获得更高的性能，在局域网中可以每小时泄露 60 比特。
通过使用 NetSpectre 攻击，攻击者可以破坏远程系统中部署的地址空间布局随
机化（ASLR）防御机制。

NetSpectre 攻击在网络上使用，需要攻击者访问受害者提供的网络接口，并且
向受害者发送大量网络数据包。这些操作不一定需要在短时间内进行。

在 NetSpectre 攻击中，攻击者需要利用两个 Spectre 组件：泄露组件和传输
组件，这些组件在收到网络数据包时执行。泄漏组件在攻击者控制的内存地址处
访问数据，并根据访问的数据改变一些微体系结构状态。传输组件可以进行任意操作，
它的运行时间取决于泄露组件修改的微体系结构状态。这些组件可以存在于网络驱动
程序，以及网络协议和网络服务的实现。

图 \ref{fig:netspectre_leak} 是一个 Spectre v1 组件，它是处理网络数据
包的程序的一部分，其中 x 由攻击者控制，它构成了 NetSpectre 的泄露组件。

\begin{figure}[htbp]
  \centering
\begin{minted}{C}
if (x < bitstream_length)
  if (bitstream[x])
    flag = true;
\end{minted}
\caption{NetSpectre 的一个泄露组件}
\label{fig:netspectre_leak}
\end{figure}

为了在远程攻击中使目标系统在推测式执行中改变微体系结构状态，攻击者采用原始
Spectre 的方法。为了触发远程系统的推测式执行，攻击者进行以下操作：

\begin{itemize}
\item 攻击者发送多个网络数据包，使得攻击者选择的 x 值总是在边界内，训
  练分支预测器在后续执行此边界检查的分支时，预测 x 在边界内
\item 攻击者发送一个数据包，使得 x 越界，\verb|bitstream[x]| 是目标系
  统中的一个秘密数据位
\item 分支预测器预测边界检查结果为真，推测式执行存储器的访问
\end{itemize}

在图\ref{fig:netspectre_leak}的代码中，虽然 flag 的值没有被修改，但是
flag 的缓存状态会发生变化。如果 \verb|bitstream[x]| 为 1,则 flag 会进
入缓存，通过 flag 的缓存状态，可以推断 \verb|bitstream[x]| 的值。

传输组件更简单，它只需要在一个操作中使用 flag，它的执行时间取决于 flag
的缓存状态，例如，最简单的一个传输组件是直接返回 flag 的值。由于传输组
件的响应时间取决于 flag 的缓存状态，因此可以泄露秘密数据位
\verb|bitstream[x]| 的值。

要完成 NetSpectre 攻击，攻击者需要测量一个秘密值泄露时的网络响应时间。
由于响应时间的差异在纳秒级，因此攻击者需要进行大量的测量，此时可以得到
明显的微体系结构状态差异。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.5\textwidth]{netspectre.eps}
  \caption{NetSpectre 的泄露组件和传输组件\supercite{netspectre}}
  \label{fig:netspectre_gadgets}
\end{figure}

\subsection{SgxPectre}

SgxPectre \supercite{sgxpectre} 将 Spectre 攻击用于泄露 Intel SGX 环境
中的数据。通过在 SGX enclave 之外污染 enclave 的 BTB 和 RSB，改变 SGX
enclave 中的控制流，将秘密数据泄露至 SGX 之外。

\subsection{MeltdownPrime 和 SpectrePrime}

MeltdownPrime 和 SpectrePrime \supercite{meltdownprime} 是一种使用
Prime+Probe 方式进行 Meltdown 和 Spectre 攻击的形式。通过利用缓存一致
性协议的缓存行失效机制，可以达到和 Flush+Reload 方式的同等精度。

\section{小结}

本章首先介绍侧信道攻击，接着分类介绍各个变体的 Meltdown 型攻击和
Spectre 型攻击，最后介绍 NetSpectre 等攻击的其他利用形式。
