5

我正在尝试在我的Kabe 湖 7600U上创建一个specpoline(cfr. Henry Wong),我正在运行 CentOS 7。

GitHub 上提供了完整的测试存储库。

我的 specpoline 版本如下(cfr.spec.asm):

specpoline:
        ;Long dependancy chain
        fld1
        TIMES 4 f2xm1
        fcos
        TIMES 4 f2xm1
        fcos
        TIMES 4 f2xm1

        %ifdef ARCH_STORE
            mov DWORD [buffer], 241     ;Store in the first line
        %endif

        add rsp, 8
        ret

这个版本与Henry Wong 版本的不同之处在于流程被转移到建筑路径中的方式。虽然原始版本使用固定地址,但我将目标传递到堆栈中。
这样,anadd rsp, 8将删除原始返回地址并使用人造的。

在函数的第一部分,我使用一些旧的 FPU 指令创建了一个长延迟依赖链,然后是一个试图欺骗 CPU 返回堆栈预测器的独立链。


代码说明

使用 FLUSH+RELOAD 1将 specpoline 插入分析上下文中,相同的程序集文件还包含:

buffer

跨越 256 个不同缓存行的连续缓冲区,每个缓存行由GAP-1行分隔,总256*64*GAP字节数。

GAP 用于防止硬件预取。

随后是图形描述(每个索引紧随其后)。

F+R 缓冲区

timings

一个由 256 个 DWORD 组成的数组,每个条目包含访问 F+R 缓冲区中相应行所需的时间(以核心周期为单位)。

flush

触摸 F+R 缓冲区的每一页(带有存储,只是为了确保 COW 在我们这边)并驱逐指定行的小功能。

“个人资料”

标准配置文件函数lfence+rdtsc+lence很好地用于分析 F+R 缓冲区中每一行的负载并将结果存储在timings数组中。

leak

这是执行实际工作的函数,称为specpoline在推测路径中放置商店和profile在架构路径中放置函数。

;Flush the F+R lines
        call flush

        ;Unaligned stack, don't mind
        lea rax, [.profile]
        push rax
        call specpoline

        ;O.O 0
        ; o o o SPECULATIVE PATH
        ;0.0 O

        %ifdef SPEC_STORE
            mov DWORD [buffer], 241        ;Just a number
        %endif

        ud2                             ;Stop speculation

   .profile:
        ;Ll Ll
        ;  !  !  ARCHITECTURAL PATH
        ;Ll Ll
        
        ;Fill the timings array
        call profile

一个小的 C 程序用于“引导”测试工具。

运行测试

代码使用预处理器有条件地在架构路径中有条件地放置存储(实际上是在规范本身中),如果ARCH_STORE已定义,则有条件地在推测路径中放置存储(如果SPEC_STORE已定义)。

两者都存储访问 F+R 缓冲区的第一行。

运行make run_spec并将与相应的符号进行汇编,编译测试并运行它make run_archspec.asm

该测试显示了 F+R 缓冲区每一行的时序。

存储在建筑路径中

 38    230    258    250    212    355    230    223    214    212    220    216    206    212    212    234
213    222    216    212    212    210   1279    222    226    301    258    217    208    212    208    212
208    208    208    216    210    212    214    213    211    213    254    216    210    224    211    209
258    212    214    224    220    227    222    224    208    212    212    210    210    224    213    213
207    212    254    224    209    326    225    216    216    224    214    210    208    222    213    236
234    208    210    222    228    223    208    210    220    212    258    223    210    218    210    218
210    218    212    214    208    209    209    225    206    208    206   1385    207    226    220    208
224    212    228    213    209    226    226    210    226    212    228    222    226    214    230    212
230    211    226    218    228    212    234    223    228    216    228    212    224    225    228    226
228    242    268    226    226    229    224    226    224    212    299    216    228    211    226    212
230    216    228    224    228    216    228    218    228    218    227    226    230    222    230    225
228    226    224    218    225    252    238    220    229   1298    228    216    228    208    230    225
226    224    226    210    238    209    234    224    226    255    230    226    230    206    227    209
226    224    228    226    223    246    234    226    227    228    230    216    228    211    238    216
228    222    226    227    226    240    236    225    226    212    226    226    226    223    228    224
228    224    229    214    224    226    224    218    229    238    234    226    225    240    236    210

存储在推测路径中

298    216    212    205    205   1286    206    206    208    251    204    206    206    208    208    208
206    206    230    204    206    208    208    208    210    206    202    208    206    204    256    208
206    208    203    206    206    206    206    206    208    209    209    256    202    204    206    210
252    208    216    206    204    206    252    232    218    208    210    206    206    206    212    206
206    206    206    242    207    209    246    206    206    208    210    208    204    208    206    204
204    204    206    210    206    208    208    232    230    208    204    210   1287    204    238    207
207    211    205    282    202    206    212    208    206    206    204    206    206    210    232    209
205    207    207    211    205    207    209    205    205    211    250    206    208    210    278    242
206    208    204    206    208    204    208    210    206    206    206    206    206    208    204    210
206    206    208    242    206    208    206    208    208    210    210    210    202    232    205    207
209    207    211    209    207    209    212    206    232    208    210    244    204    208    255    208
204    210    206    206    206   1383    209    209    205    209    205    246    206    210    208    208
206    206    204    204    208    246    206    206    204    234    207    244    206    206    208    206
208    206    206    206    206    212    204    208    208    202    208    208    208    208    206    208
250    208    214    206    206    206    206    208    203    279    230    206    206    210    242    209
209    205    211    213    207    207    209    207    207    211    205    203    207    209    209    207

我在架构路径中放置了一个商店来测试计时功能,它似乎有效。

但是,我无法通过投机路径中的商店获得相同的结果。

为什么 CPU 没有推测性地执行存储?


1我承认我从未真正花时间区分所有缓存分析技术。我希望我用了正确的名字。FLUSH+RELOAD 我的意思是驱逐一组行的过程,推测性地执行一些代码,然后记录访问每条被驱逐行的时间。

4

1 回答 1

4

您的“长 dep 链”是来自那些微编码 x87 指令的许多微指令。fcos在 SKL 上是 53-105 uop,具有 50-130 循环吞吐量。因此,每个 uop 延迟大约为 1 个周期,调度程序/预留站(RS)“仅”在 SKL/KBL 中有 97 个条目。此外,将后面的指令送入乱序的后端可能是个问题,因为微码接管了前端,需要某种机制来决定接下来要发出哪些微指令,这可能取决于某些计算的结果。(众所周知,微指令的数量取决于数据。)

如果你想从一个充满未执行的微指令的 RS 中获得最大的延迟,那么sqrtpd依赖链可能是你最好的选择。例如

    xorps  xmm0,xmm0                   ; avoid subnormals that might trigger FP assists
    times 40 sqrtsd xmm0, xmm0

    ; then make the store of the new ret addr dependent on that chain
    movd   ebx, xmm0
    ; and  ebx, 0            ; not needed, sqrt(0) = 0.0 = integer bit pattern 0
    mov [rsp+rbx], rax
    ret

自 Nehalem 以来,英特尔 CPU 可以通过分支订单缓冲区快速恢复分支未命中,该缓冲区可以快照 OoO 状态(包括 RAT 和可能的 RS)当 skylake CPU 错误预测分支时会发生什么?. 因此,他们可以准确地恢复到错误预测,而无需等待错误预测成为退休状态。

mov [rsp], rax可以在进入 RS 后立即执行,或者至少不依赖于sqrtdep 链。一旦存储转发可以产生值,retuop 就可以执行并检查预测,并在 sqrt dep 链仍在运行时检测错误预测。(ret对于负载端口 + 端口 6 是 1 个微融合 uop,其中采用分支执行单元。)

将 dep 链耦合sqrtsd到存储新的返回地址可防止ret提前执行。 在执行端口中执行retuop = 检查预测并检测是否存在错误预测。

(与 Meltdown 相比,“错误的”路径一直运行,直到故障负载达到退休,并且您希望它尽快执行(只是不退休)。但是您通常希望将整个 Meltdown 攻击置于其他东西的阴影下,像 TSX 或 Specpoline,在这种情况下,你需要这样的东西,并且整个崩溃都在这个 dep 链的阴影下。然后 Meltdown 不需要它自己的sqrtsddep 链。)


vsqrtpd ymm在 SKL 上仍然是 1 uop,吞吐量比 xmm 差,但它具有相同的延迟。所以使用sqrtsd它,因为它的长度相同并且可能更节能。)

最佳情况下的延迟是 15 个周期,而 SKL/KBL ( https://agner.org/optimize ) 上的最坏情况是 16 个周期,因此您从什么输入开始几乎无关紧要。


我最初使用 sqrtpd 得到了类似的结果。但是,我没有初始化用作输入(和输出)的 XMM 寄存器,认为这无关紧要。我再次测试,但这次我用两个双倍值 1e200 初始化了寄存器,我得到的是一个间歇性的结果。有时这条线是推测性的,有时不是。

如果 XMM0 保持次正规(例如位模式是一个小整数),sqrtpd 需要一个微码辅助。(fp_assist.any性能计数器)。即使结果正常但输入不正常。我用这个循环在 SKL 上测试了这两种情况:

  pcmpeqd   xmm0,xmm0
  psrlq     xmm0, 61        ; or 31 for a subnormal input whose sqrt is normalized
  addpd     xmm0,xmm0       ; avoid domain-crossing vec-int -> vec-fp weirdness

  mov   ecx, 10000000
.loop:
    sqrtpd  xmm1, xmm0
    dec    ecx
    jnz   .loop

 mov eax,1
 int 0x80   ; sys_exit

perf stat -etask-clock,context-switches,cpu-migrations,page-faults,cycles,branches,instructions,uops_issued.any,uops_executed.thread,fp_assist.any显示非正规输入每次迭代 1 次辅助,951M发出微指令(每次迭代约 160 个周期)。因此,我们可以得出结论,在这种情况下,微码辅助sqrtpd大约需要 95 微秒,并且当它背靠背发生时,吞吐量成本约为 160 个周期。

与输入 = NaN(全一)的 20M 微指令相比,每次迭代有 4.5 个周期。(循环运行 10Msqrtpd微指令和 10M 宏融合 dec/jcc 微指令。)

于 2019-04-05T23:07:52.687 回答