【发布时间】:2019-06-29 16:05:09
【问题描述】:
我已经阅读了一些有关 Spectre v2 的内容,显然您得到了非技术性的解释。 Peter Cordes 有更深入的explanation,但它并没有完全解决一些细节。注意:我从未执行过 Spectre v2 攻击,因此我没有动手经验。我只阅读了有关该理论的内容。
我对 Spectre v2 的理解是您做出了间接分支错误预测,例如 if (input < data.size)。如果间接目标阵列(我不太确定细节——即为什么它与 BTB 结构分开)——在解码时重新检查间接分支的 RIP——不包含预测,那么它将插入新的跳转 RIP(分支执行最终将插入分支的目标 RIP),但目前它不知道跳转的目标 RIP,因此任何形式的静态预测都不起作用。我的理解是,它总是会预测新的间接分支不会被采用,当端口 6 最终计算出跳转目标 RIP 和预测时,它将使用 BOB 回滚并使用正确的跳转地址更新 ITA,然后更新本地和全局分支历史寄存器和相应的饱和计数器。
黑客需要训练饱和计数器以始终预测采取哪些,我想,他们通过在循环中多次运行if(input < data.size) 来做到这一点,其中input 设置为确实小于data.size(相应地捕获错误)并在循环的最后一次迭代中,使input 大于data.size(例如1000);间接分支将被预测并跳转到发生缓存加载的 if 语句主体。
if 语句包含secret = data[1000](包含秘密数据的特定内存地址 (data[1000]) 旨在从内存加载到缓存),然后将推测性地分配给加载缓冲区。前面的间接分支还在分支执行单元中等待完成。
我相信前提是需要在加载缓冲区因错误预测而被刷新之前执行加载(分配一个行填充缓冲区)。如果它已经被分配了一个行填充缓冲区,那么什么也做不了。没有取消行填充缓冲区分配的机制是有道理的,因为行填充缓冲区在将其返回到加载缓冲区之后必须在存储到缓存之前挂起。这可能会导致行填充缓冲区变得饱和,因为不是在需要时解除分配(将其保留在那里以加快其他加载到同一地址的速度,但在没有其他可用行缓冲区时解除分配)。在接收到刷新不会发生的信号之前,它将无法解除分配,这意味着它必须停止执行前一个分支,而不是立即使行填充缓冲区可用于存储另一个逻辑核心。这种信号机制可能难以实现,也许他们没有想到(幽灵之前的想法),如果分支执行需要足够的时间来挂行填充缓冲区导致性能影响,它也会引入延迟,即如果data.size 在循环的最终迭代之前被有意从缓存 (CLFLUSH) 中刷新,这意味着分支执行可能需要多达 100 个周期。
我希望我的想法是正确的,但我不能 100% 确定。如果有人有什么要补充或更正的,请做。
【问题讨论】:
-
如您所知,Spectre v1 和 v2 利用 BP,细节在于 BP 的工作方式。我的印象是 v2 通过毒化 BP(通过利用并非所有 jmp 地址都用于在 BTB 中索引的事实)而不是通过错误训练它们(通过使用有效输入调用函数)来工作)。当 CPU 需要刷新管道时,它可能会等待所有正在执行的微指令(即已经分派)完成(包括需求负载),我认为它不会中途停止微指令,因为可能会有一些状态管理。所以分派负载就足够了。
标签: x86 intel cpu-architecture branch-prediction spectre