summaryrefslogtreecommitdiff
path: root/chap/chap4.tex
diff options
context:
space:
mode:
Diffstat (limited to 'chap/chap4.tex')
-rw-r--r--chap/chap4.tex197
1 files changed, 163 insertions, 34 deletions
diff --git a/chap/chap4.tex b/chap/chap4.tex
index 5eb1823..f1c7e0a 100644
--- a/chap/chap4.tex
+++ b/chap/chap4.tex
@@ -2,12 +2,10 @@
本章讲解本文提出的一种防御 Spectre 攻击的方法,该方法使用动态信息流追
踪的方法,检测 Spectre 组件指令流中可能泄露秘密数据的访存指令,并使用
-InvisiSpec 的方法执行这些访存指令。
+一种安全的方法执行这些访存指令。
-%这个设计有如下特点:
-%\begin{itemize}
-%\item 它不需要任何软件修改
-%\item
+这种微架构设计不需要软件的支持,未修改的软件和操作系统可以直接在采用这
+种微架构改进的处理器中执行。
\section{威胁模型}
@@ -29,18 +27,31 @@ Spectre-RSB 攻击,此方法可以扩展至 Spectre-STL 攻击。攻击者在
DIFT 可以作为 Spectre 攻击的检测手段之一。Spectre 的论文中指出处理器可
以追踪数据是否在推测式执行中获取,进而阻止在后续可能泄露这个数据的操作
-中使用,作为阻止数据进入隐蔽信道的方法。\supercite{spectre}
-CSF\supercite{context-sensitive-fencing} 中的译码级信息流追踪框架 DIFT,
-用于追踪处理器使用的数据是否来源于用户输入,从而处理器可以根据此信息判
-断是否需要插入 fence 微码。OISA\supercite{oisa} 在指令系统的定义中即包
-含了 DIFT 技术,用于追踪一个数据是否为秘密数据。
-
-在本文的工作中,DIFT 的作用是在运行时动态识别可能泄露秘密数据的指令。
-以图\ref{lst:spectre_v1}的 Spectre v1 组件代码为例。攻击者希望通过 Spectre
-攻击泄露 \verb|array1[x]| 的值,方法是让处理器在分支的推测式执行中,访问 \verb|array2[array1[x] * 4096]|,
-从而将一个依赖于 \verb|array1[x]| 的地址处的数据写入了缓存中。使用了基于 DIFT 的检测手段后,
-可以识别出 \verb|array2[array1[x] * 4096]| 的地址依赖于 \verb|array1[x]| 的值,
-从而处理器可以阻止对这个地址的访问。
+中使用,作为阻止数据进入隐蔽信道的方法\supercite{spectre},本文所用的方
+法则是这种观点的一种具体设计。在其他使用了 DIFT 的防御 Spectre 的设计中,
+所追踪的对象有所不同。CSF\supercite{context-sensitive-fencing} 中的译码
+级信息流追踪框架 DIFT,用于追踪处理器使用的数据是否来源于用户输入,从而
+处理器可以根据此信息判断是否需要插入 fence 微码。OISA\supercite{oisa}
+在指令系统的定义中即包含了 DIFT 技术,用于追踪一个数据是否为秘密数据。
+
+和 Conditional Speculation
+\supercite{conditional-speculation} 的 TPBuf 过滤器一样,本文的工作同样
+实现了一个用于识别一条指令是否泄露数据的方法。和 TPBuf 不同的是,基
+于DIFT 的方法并不检测不同的访存指令之间的地址关系,而是直接寻找读取数据
+的指令和泄露数据的指令之间直接或间接的依赖关系。TPBuf 过滤器不能防范非
+共享内存区域的缓存侧信道攻击,但是基于 DIFT 的方法可以。
+而SpectreGuard\supercite{spectreguard} 中可以不依赖软件实现
+的 SG(Full)方案,目的也是阻止推测式执行中从存储系统读取的数据不被泄露,
+方法是直接禁止读出的数据被后续的推测式执行的指令使用,本文的方法允许这
+样的数据被使用,但是使用这个数据的指令需要用不产生侧信道的方式执行。
+
+在本文的工作中,DIFT 的作用是在运行时动态识别可能泄露秘密数据的指令。以
+图\ref{lst:spectre_v1}的 Spectre v1 组件代码为例。攻击者希望通
+过 Spectre攻击泄露 \verb|array1[x]| 的值,方法是让处理器在分支的推测式
+执行中,访问 \verb|array2[array1[x] * 4096]|,从而将一个依赖
+于 \verb|array1[x]| 的地址处的数据写入了缓存中。使用了基于 DIFT 的检测
+手段后,可以识别出 \verb|array2[array1[x] * 4096]| 的地址依赖
+于 \verb|array1[x]| 的值,从而处理器可以阻止对这个地址的访问。
为了达到这个目的,我们为所有的物理寄存器都添加一个标记,用于表示它的值
是否来源于推测式执行中,从内存中读出的值。在上述例子中,有一条将内
@@ -96,10 +107,6 @@ lea rdx, {[}rip + 0x2b425d{]} & rdx <- rip + 0x2b425d & T{[}rdx{]} <- T{[}rip{]}
\label{tab:spectre_dift}
\end{table}
-\Todo: 解释为什么使用这种方法,和其他相似方法(DLIFT, TPBuf, SG(Full))的比较
-
-\Todo: 增加结构示意图和代码描述
-
以下描述这个检测方法的具体细节。
标记的设置:在推测式执行时,对于所有从内存读取数据的指令,将其所有目的
@@ -141,7 +148,7 @@ lea rdx, {[}rip + 0x2b425d{]} & rdx <- rip + 0x2b425d & T{[}rdx{]} <- T{[}rip{]}
的情形,如图\ref{lst:victim_v10}的例子\supercite{msvc}:
\begin{figure}[htbp]
-\begin{minted}{C}[frame=single,linenos=true]
+\begin{minted}[frame=single,linenos=true]{C}
void victim(size_t x, uint8_t k) {
if (x < array1_size) {
if (array1[x] == k)
@@ -175,14 +182,27 @@ void victim(size_t x, uint8_t k) {
\label{fig:spectre_dift}
\end{figure}
+\section{不安全指令的执行方式}
-\section{使用推测式执行缓冲区}
-
-\Todo: 说明在检测到不安全的访存指令后,推迟它的执行和用 InvisiSpec 的 SpecBuf 执行的区别
+对于检测到的不安全的指令,最简单的方式是阻止它的执行,直到指令因推测式
+执行错误回卷,或确认为安全状态,再重新调度至执行单元执行。这种做法会导
+致推测式执行中,后续所有依赖于这条指令的指令都会被推迟,降低流水线的利
+用率,导致性能下降。
-\Todo: 为什么只对部分指令使用,对推测式执行中的部分指令使用 InvisiSpec 是否需要修改
+一种方法是使用 InvisiSpec \supercite{invisiSpec},和 Spectre 攻击的检测
+机制结合后,可以作为单条不安全指令的执行机制。它将不安全指令从内存中读
+到的数据放入推测式执行缓冲区中,后续的指令可以使用这条指令的执行结果,
+确保指令流的继续执行。在推测式执行错误时,推测式执行缓冲区的内容会被丢
+弃,缓存状态不会改变,因此不会产生侧信道。推测式执行被验证为安全时,缓
+冲区中的数据将会更新至存储系统。为类确保不发生访存违例 InvisiSpec 需要
+在确认指令安全后,对读到的数据做一次验证,因此需要再次访问存储系统。
-\Todo: 本文使用的基于 DIFT 的检测方法和 InvisiSpec 结合
+由于缓存命中的指令不会改变缓存的内容,因此还有一种方法是在推测式执行时,
+如果一级缓存命中,则允许该指令继续执行,否则中止该指令的执行,等待指令
+被回卷或转为安全的指令。如果在推测式执行时,缓存命中率高,则这种方案不
+会阻止后续指令的执行,如果程序在推测式执行中缓存命中率低,则性能损失较
+大。这种方法和 Conditional Speculation
+\supercite{conditional-speculation} 中的基于缓存命中的过滤器基本相同。
\section{针对 Spectre 攻击的微架构在 gem5 中的实现}
@@ -221,9 +241,9 @@ gem5 的发射、执行和回写三个阶段由一个类 DefaultIEW 实现,它
gem5 的提交阶段由 DefaultCommit 类实现,它提交 ROB 队列头部的指令,更
新 ROB 的状态。
-\subsection{InvisiSpec的LSQ的实现}
+\subsection{安全执行装载指令方案的实现}
-\Todo: InvisiSpec 所用的 load 指令执行逻辑在 gem5 LSQ 中的实现
+图\ref{fig:load_exec}是 gem5 中执行一条装载指令的总体流程。
\begin{figure}[htbp]
\centering
@@ -232,16 +252,125 @@ gem5 的提交阶段由 DefaultCommit 类实现,它提交 ROB 队列头部的
\label{fig:load_exec}
\end{figure}
-\subsection{推测式执行缓冲区的实现}
+在执行阶段,处理器将装载指令传递至 LSQ,装载指令将会按序进入 LQ. gem5
+用指令系统特定的 initiatAcc 方法模拟该指令系统中装载指令访问存储系统的
+方式。最后 LSQ 将装载指令的信息封装为一个请求,用 sendTimingReq 将这个
+请求发送至存储系统。
+
+发送至存储系统的读请求并不会立刻得到结果,需要等待来自存储系统的响应。
+图\ref{fig:load_exec}的右半部分是处理器接收响应的流程,
+在 recvTimingResp 后,处理器可以得到此前发送的请求对应的结果,根据得到
+的结果,LSQ 执行回写操作,将结果写入至指令的目的寄存器,并将指令送至提
+交阶段,完成指令的执行。
+
+对于不安全指令的执行,可以为其增加一种 SpecLoad 请求,不同于普通的Load
+请求,SpecLoad 执行后,不会改变任何缓存状态和缓存一致性状态,因此不会留
+下缓存的侧信道。
-\Todo: InvisiSpec 的 SpecBuf 在 gem5 中的实现
+% 一致性协议
-\subsection{InvisiSpec对缓存一致性协议的修改}
+对于 InvisiSpec,在指令确认安全后,还需要向存储系统再发一个请求,以验
+证 SpecLoad 读取的结果正确,或直接确认 SpecLoad 的结果,因此还需要再增
+加一个 Expose 请求,用于确认或验证结果。
-\Todo: InvisiSpec 的 SpecBuf 在 gem5 中的实现
+在 gem5 中,多核系统的缓存和缓存一致性模型由 Ruby 存储系统模拟。本文模
+拟一个带二级缓存的处理器,缓存一致性协议为 MESI,需要修改的相关文件
+为 src/mem/protocol 下以 MESI\_Two\_Level 开头的文件。
+
+对于一级缓存,为了增加对不安全内存读取的支持,我们修改状态机文
+件 MESI\_Two\_Level-L1cache.sm. 除了添加 SpecLoad 和 Expose 两个请求外,
+还要处理从其他处理器转发的响应的请求。此外,在处理器的实现中,状态机接
+收到处理器的请求,到从存储系统得到数据之间,状态机处于一个过渡状态,因
+此在状态机中添加一个状态 IX,表示接收了 SpecLoad 之后缓存缺失,向下级存
+储系统发送读请求,还未得到响应。
+
+InvisiSpec 中,一级缓存处理 Load 和 Expose 的部分状态如
+图\ref{fig:invisispec_mesi}所示。
+
+\begin{figure}[htbp]
+ \centering
+ \includegraphics[width=0.8\textwidth]{invisispec_mesi.eps}
+ \caption{InvisiSpec 一级缓存处理 Load 和 Expose 请求}
+ \label{fig:invisispec_mesi}
+\end{figure}
+
+其中,对于任意一种状态,接收到 Expose 请求后,处理器都会更新缓存状态,
+将指令从存储系统中读到的数据写入缓存。对于 SpecLoad 缓存命中的情
+形,spec\_load\_hit 取出缓存行,返回至处理器,和 load\_hit 的区别是推测
+式执行中访问缓存,不会更新缓存替换算法的状态,避免产生侧信道信息。
+
+% hit based
+
+而对于只在缓存命中时执行装载指令的方案,设计相对简单。它只需要处理
+SpecLoad 请求。
+
+\subsection{执行流水线的修改}
+
+我们在 DynInst 类为指令添加新的状态和属性,添加的主要的指令状态和属性
+见表\ref{tab:inst_status}。
+
+\begin{table}
+\begin{tabular}{|p{0.25\textwidth}|p{0.75\textwidth}|}
+\hline
+指令状态和属性 & 含义\tabularnewline
+\hline
+\hline
+PrevBrsResolved & 记录指令之前的分支是否得出结果\tabularnewline
+\hline
+IsTainted & 在基于 DIFT 的检测方案中,记录指令是否依赖于被标记的寄存器\tabularnewline
+\hline
+AfterTaintedBranch & 在基于 DIFT 的检测方案中,记录指令是否在被标记的转
+ 移指令之后\tabularnewline
+\hline
+L1Hit & 记录指令的 SpecLoad 请求是否命中一级缓存并且缓存内容不变,如果
+ 是,则不需要发送验证请求\tabularnewline
+\hline
+ExposeSent & 已发送 Expose 请求\tabularnewline
+\hline
+ExposeCompleted & Expose 已执行完成\tabularnewline
+\hline
+ValidationCompleted & 数据已验证完成\tabularnewline
+\hline
+NeedPostFetch & 需要再次发送 Expose 请求\tabularnewline
+\hline
+ReadyToExpose & 指令已确认安全,可以发送 Expose 请求,或直接用基本的方
+ 式执行指令\tabularnewline
+\hline
+NeedExposeOnly & 只需要发送 Expose,而不需要通过再次读取数据用于验证\tabularnewline
+\hline
+FenceDelay & 指令被阻止执行,直到确认安全,在 InvisiSpec 之外的执行方
+ 案中使用\tabularnewline
+\hline
+\end{tabular}
+\caption{新增的指令状态}
+\label{tab:inst_status}
+\end{table}
+
+在每一周期,处理器扫描 ROB 中的每条指令,更新流水线中每条指令
+的 PrevBrsResolved 状态。在 LSQ 中,对于每一个未完成的 load 指令,根据
+指令之前是否存在未决分支,判断指令是否安全,设定 ReadyToExpose 属性,取
+消 FenceDelay 标志。
\subsection{动态信息流追踪的实现}
-\Todo: 本文所用的 DIFT 方案在 gem5 中的实现,和已有 InvisiSpec 代码的结合
+动态信息流追踪的实现分为标记设置、传播、清除和使用四个部分,以下分别介
+绍它们在 gem5 中的实现。
+
+对所有推测式执行中的装载指令,其目的寄存器都需要设置标记。在扫描 LSQ
+中指令的时候,指令前存在未决分支,则对这个指令的目的寄存器设置标记。
+
+标志的传播在执行阶段进行。对于非访存类指令,如果指令有设置了标记的源寄
+存器,则设置该指令的目的寄存器的标记。同时也设置指令的 IsTainted 属性,
+它作为转移指令的标记。
+
+指令的 IsTainted 属性和目的寄存器的标记在更新 ROB 中每条指令
+的 PrevBrsResolved 状态的时候清除,这个过程同时更新指令
+的 AfterTaintedBranch 属性。
+
+标记的使用在更新 LSQ 中指令状态的时候发生,在加入动态信息流追踪的设计中,
+指令的 ReadyToExpose 属性设定的条件有:(1)指令已设置
+了 PrevBrsResolved 属性,或者(2)AfterTaintedBranch 未设置,并且源寄存
+器均未被标记。
+
\Todo: 如何添加一个总结性的章节?