本文主要是介绍InvisiSpec Making Speculative Execution Invisible in the Cache Hierarchy,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
InvisiSpec: Making Speculative Execution Invisible in the Cache Hierarchy
-
摘要:
- 推测式执行对微架构的任何状态改变都可能会泄露信息
- 论文提出了InvisiSpec,通过在数据缓存的层级结构中使得推测执行不可见,从而抵御硬件的推测攻击。主要是阻断了利用数据缓存进行隐蔽信道或侧信道来传递推测式load泄露的数据
- 在invisiSpec中,不安全的推测式load的数据会被放入一个新的结构,称为speculative buffer(推测缓冲区)。当load确定是安全的(推测执行结束),此时系统其它部分才可以看到该load
- InvisiSpec会识别可能违反内存一致性的load,强制它们执行验证过程,保证正确性
- 论文提供了两种InvisiSpec的设计,分别针对Spectre攻击和Futuristic攻击。相对于使用栅栏来防御攻击,前者将执行速度降低74%改善到了21%,后者从208%降到72%
-
介绍:
- 对于熔断攻击可以将触发异常的load指令获取的数据标记为不可用。但是对于不会引发异常的Spectre攻击就需要其它解决办法,此时的两个挑战:
- 任何推测执行的指令都可能产生攻击
- 任何会改变微结构状态的推测执行代码都可以创建隐蔽信道或者侧信道
- 针对于幽灵变种1的软件缓解方法,问题:性能损失,只针对于branch
- 插入FENCE指令
- 插入CMOVG类似的指令,将load的访问边界限制在一个范围内。即当address>address_bound,则把address设置为这个边界值。这个边界值会比数组本来的范围更大(为了消除代码歧义),但是仍旧不会大太多,因此会将访问范围限制在某一个区域
- InvisiSpec:通过让推测执行在数据缓存中不可见来抵御硬件推测执行攻击
- 阻止微架构的隐蔽信道和侧信道攻击,例如cache的load,cache set 的占用情况,行替换信息,cache一致性协议状态等
- 目的不仅仅是防范基于分支的spectre攻击,同时希望防范任何推测式load所带来的未来攻击
- InvisiSpec的微架构的两种机制:
- 不安全的load读取到的数据会放入新的推测式缓冲区中(SB),不会修改缓存层次结构
- 当推测式load确定是安全时,通过硬件将这些数据重新将其放回memory和cache中,使其对所有部分可见。如果这个过程中,发现可能违反内存一致性,则验证第一次load的数据是否正确,如果不正确需要将该load压缩,重新执行
- 对于熔断攻击可以将触发异常的load指令获取的数据标记为不可用。但是对于不会引发异常的Spectre攻击就需要其它解决办法,此时的两个挑战:
-
威胁模型(Threat Model)
- 推测式执行攻击利用了在暂态指令遗留的副作用。受害者程序推测执行某些访问load指令,将敏感数据写入寄存器或者存储队列,并且被进一步的推测执行通过隐蔽信道传递给攻击者。这些指令都是推测执行的,即在实际代码执行过程中,不应该出现
- 基于cache层次结构的侧信道:大部分的攻击利用的是推测执行的load会修改cache的状态。其中基于cache的最为常见的两种技术Prime+Probe(接收方通过自己的数据被evicted的情况来监视cache的冲突)和Flush+Reload(接收方使用类似于clflush指令清除受害者的cache line)
- 信道保护:
- 目前利用cache层次结构构建隐蔽信道和侧信道的方式:cache状态(例如占用情况等),替换信息,一致性信息(cache line所处的一致性状态)以及d-TLB的状态(存在哪些表项)
- InvisiSpec的目标是为数据缓存层次结构提供一个完整的防御,但是不排除可以保护其它信道
- 不研究的范围:物理的侧信道攻击,以及一些其它的侧信道攻击,例如执行部件的执行时间变化等
- 前提设置,在这些设置中,屏蔽基于cache层次结构的侧信道和隐蔽信道攻击
- 同一个线程:攻击者和受害者在同一个线程中,共享私有缓存和tlb,并且当这些资源处于活动状态时,攻击者可以快照这些资源状态。另一方面攻击者不可能了解细微的行为,例如具体的执行单元何时被使用
- 不同的物理核:攻击者运行在分开的物理核中,例如多租户的云场景中,不同的VM运行在不同的物理内核上。此时攻击者监控LLC及它的核的一致性目录。
- SMT:攻击者在同一个核的相邻SMT上下文中运行,共享私有cache和TLB
-
推测执行攻击
-
利用异常的攻击:异常指令需要到达ROB的头部是时才会处理异常,这期间,其它指令会泄露数据
- meltdown:在用户权限下,访问kernel的地址数据
- L1TF(L1 Terminal Fault):读取一个虚拟地址对应的物理地址数据,尽管此时该虚拟地址所在的页已经不在memory中
-
控制流的错误预测:
- 幽灵变种1:通过数据边界检查的错误预测,读取数组越界的数据
- 其它变种:利用间接分支错误预测,返回地址错误预测,重写RAS,从而读取任意的内存数据
-
存储相关性推测:SSB(speculative store bypass)攻击利用推测的load,通过旁路得到地址尚未解析的store的数据,然后在依赖于之后的指令将得到的数据泄露出去。
-
-
InvisiSpec的主要思想
- 严格而言任何的load指令,在达到ROB的头部之前就获取到数据都称为推测式load。论文主要研究那些可能会产生安全问题的推测式load,称之为USLs(unsafe speculative loads)
- 在幽灵中,USLs为跟随者尚未解决的分支指令之后的推测load;在论文提出的未来的攻击模型中,USLs是那些会由于之前的指令而被压缩的推测load
- 使得USLs不可见:USLs不能以任何其它线程可见的方式修改缓存层次结构,包括一致性状态。因此将USL的数据load到一个特殊的buffer中(推测缓冲)。当USL称为安全的load的时候,需要使得USBL可见,即使得内存层次结构中的USL的所有副作用对其他所有线程可见。(通过re-load的方式,使得USL可见)
- 保持存储一致性:由于从USL不可见到可见的状态,USL在SB中不会改变仍和一致性状态,也无法处理任何指向USL的数据的失效操作。此时load如果违反了存储一致性协议将有可能无法检测。因此在使得USL可见时,即re-load时,需要执行一个验证操作。(举例,USL在SB的数据,在L1中也有,但是L1的数据被设置为了无效,但是USL则没有这个操作)
- USL的验证或者公开
- 验证:将USL使用的实际数据和从cache层次结构中load的最新数据(re-load)进行比较,如果不同,USL及其后续所有指令都会被压缩。代价太高(在这个过程中,USLs可能会阻塞流水线)
- InvisiSpec发现很多USL在不可见的时间内不会违反内存一致性模型,从而可以使用代价更小的公开方式来使其可见。这种公开的方式直接将SB中的数据放入cache的层次结构中,不需要比较,并且这些USL指令会在将数据发送到cache时,立即retired,从而不阻塞流水线
-
InvisiSpec的操作
- 对于一个load在InvisiSpec中有两个步骤
- 访问cache的层次结构获取当前最新的cache line的数据,然后只存储在SB中,并且不修改cache的一致性状态,替换策略状态或者其它的cache状态。其它线程都不可见,core继续执行。SB中存储一个cache line,以利用空间局部性
- 当USL可以成为可见的,并且在该数据收到新的请求之后,硬件触发验证或者公开事务,再次对该line重新请求,并且修改本地缓存
- 如果同时有多个USL需要进行验证或者公开,此时验证过程必须按序进行,公开过程可以重叠。
- 对于幽灵攻击,只有当分支指令被解决之后,才能够使得USL可见;对于未来的攻击方式,只有当USL指令处于ROB头部,或者不会再被squashed之后才能够可见
- 流水线可能停顿的时机:USL指令正在被重新验证,并且USL指令处于ROB的头部,ROB已经没有空闲表项,则此时流水线会停顿(之后会使用硬件上的中断屏蔽,来保证USL在ROB虽然不在头部,但是不再会被压缩,从而减少USL处于ROB的头部的概率)
- 对于一个load在InvisiSpec中有两个步骤
-
InvisiSpec中何时使用验证操作和公开操作(取决于内存一致性模型)
- TSO模型中:
- 当ROB中没有更早的load或者fence指令在USL之前,则USL不会由于之后的无效操作而被squashed,此时在可见时可以使用公开操作
- 当ROB中有更早的load或者fence指令,则需要在可见时使用验证操作
- RC模型中:只有当ROB中存在更早的fence指令,此时才需要验证,其它都只需要公开即可
- InvisiSpec中为了减少TSO模型中的验证操作而导致的流水线停顿的两种方法
- 将一个需要验证的USL转换为只需要公开的USL。根据要求,当ROB中存在更早的load,则此时USL需要被标记为验证,但是如果此时更早的load已经获取到了数据尤其是这些load是USL,并且请求的数据已经在寄存器中,则此时USL只需要被标记为公开。
- 将需要验证的USL早点squashing。当core接收到一个无效操作,并且这个操作的数据正好也在SB中,并且SB中该数据对应的USL已经被标记为验证,则此时可以squashing这个USL。当SB中有两个USL指向同一个数据,并且都被标记为验证,此时更晚一些的USL可以被压缩
- 验证和公开操作的重叠:为了提高性能,尽可能的重叠进行这两个操作
- 在重叠执行多个验证操作和公开操作期间,必须强制执行内存一致性模型
- 在重叠执行多个验证操作和公开操作期间,任何被压缩的操作都不能改变缓存的状态(只要求在针对未来攻击的方式中需要保证,因为在幽灵中,此时所有的load都是正确指令,只是违反了存储一致性协议)
- 为了确保之前两个要求满足,需要的一些条件:针对于不同USL的验证和公开操作必须按程序顺序执行;对于一个USL的验证操作,在此USL之后的验证和公开操作不能够与之重叠(防止squash时,cache的其它状态会被之后的公开验证操作改变)
- TSO模型中:
-
InvisiSpec中的SB
- 重用SB中的数据:
- USL会将一个完整的cache line放入SB中,并设置一个地址掩码,用来标识将该行的哪一部分数据传递给寄存器
- 如果一个USL发现之前的一个USL已经请求或者已经将数据放入SB中(数据都在一个cache line中),则此时当前的USL不会发出请求,而是等待之前的USL完成请求操作,然后将该数据复制到自己的表项中,然后设置地址掩码
- 减少主存访问次数
- 当一个USL访问,发现LLC miss,此时将会访问主存,并且很有可能之后在验证或者公开的时候也会出现LLC miss
- LLC-SB(LLC speculative buffer),每个核一个,在LLC旁边,用于存放USL从主存中访问的数据的副本,当USL进行验证或者公开操作时,再从LLC-SB获取。如果在不可见的期间,第二个core使用验证/公开操作或者安全的访问该cache line,则将第一个core的LLC-SB中的该行设置为无效,确保数据必须是有效的
- 重用SB中的数据:
-
L1中的Speculative Buffer
- SB的设计:靠近core,降低访问延迟,保证操作简单。因此将其设计为与LQ有一样多的表项,同时LQ和SB之间一对一映射
- 对于一个给定的LQ表项,可以很快的找到SB表项。同时由于LQ很容易找到对同一地址的访问,因此也可以很快在识别出SB中对于同一cache line的多个表项
- 这种方式可以简化分配SB表项,删除提交load的SB表项和被压缩的load的表项(类似于LQ)
- SB中只包括了数据和地址掩码,但是可以使用LQ中的一些状态位指示一些信息
- LQ和SB的操作
- load指令被发射:分配一个LQ表项和SB表项,设置LQ的valid位为1
- load指令的地址被计算得到:load准备发往cache。如果load是安全的,则SB表项将不会被使用,LQ中的State被设置为N;否则load为USL,State被设置为E/V(公开/验证),然后使用一个Spec-GetS事务完成数据的请求,但是不改变cache状态。如果发生miss,还会进一步修改MSHR来等待请求完成。当有之前的store数据相同时,会利用bypass先拿到数据,放入寄存器,但是spec-gets请求仍然需要完成,因为SB是一个cache line,但是结果写回时不能覆盖之前放入sb表项中的部分数据
- USL的请求到达Core:数据被复制到SB表项中,请求的数据放入目标寄存器(除了已经利用bypass获取)。设置LQ的Performed位
- USL到达可见点:根据E/V进行验证操作或者可见操作(此时MSHR依旧可以使用)
- 响应公开或者验证的到达的请求:对于可见操作,如果USL尚未提交,此时将LQ中的state设置为C(验证/公开完成)。对于验证操作,比较请求得到的数据是否相同,如果相同,设置state为C,否则将所有随后的指令压缩,移动LQ和SB的尾指针
- 接收到一个cache line的无效操作:根据之前的方式,优化SB中的验证操作的USL
- SB的设计:靠近core,降低访问延迟,保证操作简单。因此将其设计为与LQ有一样多的表项,同时LQ和SB之间一对一映射
-
一些其它实现上的细节
- cache一致性协议的变化:为了支持Spec-GetS操作,获取数据,并且不修改cache的状态
- 原子指令:原子指令需要引入写操作,因此InvisiSpec中会延迟执行,然后在执行原子操作
- 保护d-tlb:防止USL明显的改变D-TLB的状态,如果发生tlb miss,则通过页表查询等待USL到达可见点,如果在此之前,USL被压缩了,则停止页表查询;如果发生tlb hit,所有会修改tlb的操作都会等待USL到达可见点之后在进行
-
安全分析:SB和LLC-SB不会产生新的侧信道。论文分析了攻击者能否通过发射器来传递信息,例如加快攻击者执行或者减慢攻击者执行
-
实验:gem5, spec2006, parsec.
这篇关于InvisiSpec Making Speculative Execution Invisible in the Cache Hierarchy的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!