% 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 是一种新型攻击，它使得任何用户进程可以简单地读取所在机器的整个
内核内存（包括映射在内核区域中的所有物理内存），从而完全破坏内存隔
离。Meltdown 不利用任何软件漏洞，适用于所有主流操作系统。Meltdown 利用
大多数现代处理器中侧信道信息，可以用于 2010 年以来的现代 Intel 处理器，
也可能可用于其他厂商的处理器。

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

乱序执行是现代处理器用于提高性能的重要特性，用于隐藏功能单元的延迟，例
如，读取内存的功能单元需要等待存储器中的数据到达。现代处理器在遇到长延
迟的执行时，不会停止执行，而是可以乱序操作，将后续的操作调度到空闲的功
能单元中。然而，这些操作通常有预期之外的副作用，例如，在顺序和乱序执行
中，时间差异都可能泄露信息。

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

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

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

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

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

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

以下是一段 Meltdown 攻击的示例代码，通过它可以读取内核地址空间的数据：

\begin{minted}{nasm}
xor rax, rax
retry:
mov al, byte [rcx] ; 读取内核空间的数据
shl rax, 0xc
jz retry
mov rbx, qword [rbx + rax] ; 访问探测数组
\end{minted}

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

\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

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-NG 包含了 Foreshadow-OS 和 Foreshadow-VMM.

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

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

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

% foreshadow-VMM

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

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

\begin{figure}[htbp]
  \centering
  \includegraphics[width=0.8\textwidth]{foreshadow-vmm.eps}
  \caption{Foreshadow VMM 的一种使用场景。同一物理核上的虚拟机共享一级
    缓存，攻击者可以操作其内存访问，读取受害者虚拟机在一级缓存的内容。}
  \label{fig:foreshadow-vmm}
\end{figure}

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


\subsubsection{LazyFP}

由于浮点寄存器和内存交换数据进行操作有一定开销，大部分应用程序不做浮点
运算，因此部分操作系统在进行上下文切换时不保存浮点寄存器。操作系统在恢
复用户态程序的运行之前，对处理器设置禁用浮点寄存器，程序使用了浮点指令
后，触发异常，操作系统才对浮点寄存器进行保存和恢复。
LazyFP\supercite{lazyfp} 攻击利用这个异常，利用这个方法，攻击者可以读
出一个处理器核此前运行的使用了浮点寄存器的程序中的浮点寄存器的值。

\subsection{Spectre型攻击}

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

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

\subsubsection{Spectre-PHT}

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

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

\begin{minted}{C}
if (x < array1_size)
    y = array2[array1[x] * 4096];
\end{minted}

这段程序的 if 条件语句用于判断用户给定的下标 \verb|x| 是否在 array1 界
内，如果在界外，则无法读取 \verb|array1[x]|. 但是在推测式执行中，瞬时
指令可以执行读取 \verb|array1[x]| 的操作，并且继续利用读取的值用于访问
\verb|array2|，导致一个依赖于 \verb|array1[x]| 的地址出现在缓存中。

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

\begin{minted}{C}
if (x < len)
    a[x] = z;
\end{minted}

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

\subsubsection{Spectre-BTB}

Spectre-BTB 利用间接转移，包含的攻击为 Spectre v2. 攻击者寻找受害者地
址空间中的一个组件（gadget），它可以通过隐蔽信道泄露受害者地址空间中的
数据。攻击者训练处理器的 BTB 使得受害者的间接转移指令的预测的目标地址
为该组件的地址，从而受害者推测式执行这个组件的指令。训练 BTB 的过程可
以在攻击者的地址空间中进行。

\subsubsection{Spectre-RSB}

返回栈（RSB）用于预测程序函数调用的返回地址。如果 RSB 中的返回地址和实
际返回地址不同，则会产生错误的推测式执行，
SpectreRSB\supercite{spectre-returns}\supercite{ret2spec} 利用了这种错
误的推测式执行进行攻击。

Spectre-RSB 的利用方式包括使 RSB 溢出，及污染 RSB. RSB 的大小为普通程
序函数调用深度，调用深度超过 RSB 的大小时，会造成RSB 溢出，覆盖其中最
老的条目，在函数返回时，会发生 RSB 下溢出。而有的处理器在 RSB 为空时，
使用 BTB 进行转移预测，攻击者可以用 Spectre-BTB 的攻击方式进行攻击。污
染 RSB 的方法包括在处理器的不同上下文中使用 RSB，推测式污染 RSB 等。

\subsubsection{Spectre-STL}

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

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

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

\subsection{MeltdownPrime 和 SpectrePrime}

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

\subsection{NetSpectre}

NetSpectre \supercite{netspectre} 是通过网络使用 Spectre 的攻击方法，
这种攻击方式将 Spectre 由本地攻击扩展到远程攻击，使得不会运行攻击者控
制的代码的设备也受到 Spectre 攻击的威胁。

在 NetSpectre 攻击中，攻击者利用两中 NetSpectre 组件，一个是泄露组件
（leak gadget），通过改变微架构状态，泄露秘密数据；一个是传送组件
（transmit gadget），它将微架构状态的改变通过网络传送给攻击者，攻击者
可以通过网络数据包的延迟，推断出微架构状态，从而获取秘密数据。

NetSpectre 可以在局域网，或者 Google cloud 等云平台中使用。利用
Evict+Reload 缓存信道，每小时可以泄露 15 比特。利用基于 AVX 的隐蔽信道，
可以每小时泄露 60 比特。

\subsection{SgxPectre}

SgxPectre \supercite{sgxpectre} 将 Spectre 攻击用于泄露 Intel SGX 环境
中的数据。通过在 SGX enclave 之外污染 BTB，可以改变 SGX enclave 中的控
制流，进行 Spectre-BTB 攻击。

% vim:ts=4:sw=4
