【问题标题】:Extracting value from an MPX bounds register into a general purpose register从 MPX 边界寄存器中提取值到通用寄存器中
【发布时间】:2017-04-19 18:15:39
【问题描述】:

英特尔的内存保护扩展提供四个新的 128 位边界寄存器,BND0BND3,每个都存储缓冲区的一对 64 位下限 (LB) 和上限 (UB) 值。

如何将下限的 64 位值(即%BND0[0-63])提取到 64 位通用寄存器(即%RAX)中?

据我了解,问题在于英特尔的指令集没有提供直接执行此操作的方法。我查阅了 Intel 的 MPX 启用指南(参见 this link 底部的下载),发现有一条 BNDMOV b/m, b 指令将边界寄存器中的下限和上限存储到内存或另一个寄存器时间>。然而,似乎 另一个寄存器 本身必须是一个边界寄存器,因为不可能将 128 位边界寄存器放入 64 位通用寄存器中。

此外,据我所知,没有办法通过使用半角同级寄存器(即%EAX 包含@987654328 的下半部分的值)来获取边界寄存器的下限@)。同样,没有地址%BND#.LB,即使 fig. 14 页。 MPX 启用指南的 50 显示了一个调试器监视这些值。

【问题讨论】:

    标签: assembly x86-64 intel


    【解决方案1】:

    这是一个实验性解决方案,我手头没有支持 MPX 的 CPU。
    我的工作纯粹是理论上的,我不知道它是否会因为真实硬件上的一些小问题而惨遭失败。


    作为Igor said in is complete answer,没有指令可以在不通过内存的情况下读取bnd 寄存器。
    这就是故事的结局。

    作为Mattew expressed his concern about spilling the value of the bnd register,我想通过间接方式读取bnd 寄存器。

    最重要的是,由于 MPX 的性质,程序员允许程序员对bnd 寄存器的值执行dichotomic search

    我将在整个答案中使用bndcl 作为参考。对偶,bndcu,考虑到一个补码格式,是类似的。

    如果寄存器下界LB的值高于测试的地址A,指令bndcl会产生异常#BR。
    需要将条件 A 基本上,如果可用且可访问,bndstatus 寄存器的字段 EC(错误条件)就是我们要寻找的:如果 bndcl 失败,则为 1,否则为 0。
    或者,可以通过将变量/寄存器设置为 1 来处理 #BR 异常(或操作系统将其具体化为 bndcl 仅生成 #BF 和 #UD 的任何异常)。如果在执行 bndcl 之前重置所述寄存器,那么它将反映检查结果。
    必须小心rip 指向有问题的bndcl 指令。

    长话短说,需要类似的东西

     bndcl:
        A < LB ⇒ ECX = 1 
        A ≥ LB ⇒ ECX = 0 
    

    有了这些先决条件,我们可以对下限的值执行二分搜索。
    有一个小警告:我们无法测试相等性,只能测试大于或等于
    这意味着:1)我们执行固定数量的比较(略大于 64)和 2)我们需要额外的最后一步来确定确切的地址。

    要理解第 2 点)只需考虑在 0 到 15 的数组中搜索值 14 和 15:

           15                          14
    
    Pivot    Step   Cmp         Pivot    Step   Cmp
     8        4      ≥           8        4      ≥
     12       2      ≥           12       2      ≥
     14       1      ≥           14       1      ≥ (Can't stop here)
     15       0      ≥           15       0      ≥
    

    我的 CPU 不支持 MPX,所以我假设存在一个宏 bndclex,它执行bndcl 的检查并如上所述设置ecx

    ;Get the bnd0 lower bound without storing it into memory
    ;It uses a dichotomy search
    
    get_bnd0_lb: 
     mov rax, 4000_0000_0000_0000h  ;Half-range
     lea rbx, [rax*2]               ;Address to check
    
    
    _check:
    
     ;D O   T H E   B O U N D    C H E C K 
    
     bndclex _bnd0, rbx
    
     ;ecx = 01 if rbx < bnd0.lb
     ;ecx = 00 if rbx >= bnd0.lb
    
     ;S E T   T H E   C F 
    
     not ecx
     shr ecx, 1                     ;CF = 0   IF rbx < bnd0.lb
                                    ;     1   IF rbx >= bnd0.lb
    
     ;A D D / S U B   T H E   H A L F   R A N G E 
    
     sbb rcx, rcx           ;rcx =  0 IF rbx < bnd0.lb
                                    ;      -1 IF rbx >= bnd0.lb
    
     sub rbx, rcx                   ;rbx = rbx     IF rbx < bnd0.lb
                                    ;      rbx +1  IF rbx >= bnd0.lb
    
     xor rcx, rax                   ;rcx = rax     IF rbx < bnd0.lb
                                    ;      NOT rax IF rbx >= bnd0.lb
    
     add rbx, rcx                   ;rbx = rbx + rax      IF rbx < bnd0.lb
                                    ;      rbx + NOT rax + 1 
                                    ;    = rbx + (-rax)   IF rbx >= bnd0.lb
    
     shr rax, 1                     ;Halve half-range
    jnz _check
    
     ;C H  E C K    T H E   E X A C T   A D D R E S S
    
     bndclex _bnd0, rbx
    
     ;ecx = 01 if rbx < bnd0.lb
     ;ecx = 00 if rbx >= bnd0.lb
    
     lea rax, [rbx + rcx]
    
     ret 
    

    该算法通过将初始枢轴 P0 设置为 64 位地址范围的一半和初始 S0 步长等于地址范围。
    然后在每一步,它将枢轴移动到 Pi+1 = Pi ± Si 并将步长 Si+ 减半1 = Si / 2.

    此算法将bnd 寄存器下限中的零数泄漏为生成的异常数,但不会将值存储在内存中。
    可以生成合成#BR 异常来解决此问题。


    bndclex 宏的实现——或者更好地消除它——留给 OP,因为这在很大程度上取决于环境。
    我的很简单

    %macro bndclex 2
    
     xor ecx, ecx           ;RCX = 0
     lea r8, [rcx+1]        ;r8 = 0
    
     cmp %2, QWORD [REL %1 %+ .lb]            
     cmovb rcx, r8          ;RCX = 1 if %2 < lower bound
    
    %endmacro
    

    【讨论】:

    • 这是一种非常聪明的方法,虽然是间接的“读取”值的方法。我将不得不采用这个想法并尝试在我们原型的微基准测试中实现它,以查看执行多达 64 个二分搜索的枢轴子例程的相对性能成本是多少。仅出于聪明才智,我希望我能给予不止一张赞成票。
    • @MatthewCole :您可以悬赏这个问题,然后最终将其奖励给玛格丽特,或者甚至是更聪明的答案(如果存在的话)
    • 至于测试——英特尔 SDE 支持 MPX:software.intel.com/en-us/articles/…
    • 谢谢@MichaelPetch!我从未使用过英特尔 SDE,这很有趣。
    • 谈论矫枉过正:) 仍然,很酷的想法!我想知道这是否可以用来绕过 MPX 保护。
    【解决方案2】:

    显然你不能。如果您需要绑定寄存器的值,您似乎应该使用内存(使用BNDMOVBNDSTX)。 BND0.LB 可能只是调试器符号,而不是实际的寄存器。

    【讨论】:

    • 这就是我害怕的。我将把这个问题搁置几天,看看是否有人知道绕过指令集限制的方法。如果没有,我会接受这个答案。
    • 有什么理由你不想/不能通过记忆?在问题中提及它可能会很好。
    • 我不能透露太多细节,因为它与我们尚未发布的研究原型相关联。但简短的回答是,我们希望避免在内存中留下 MPX 寄存器中包含的值的痕迹。
    • 另外,还有一个问题是我们的原型不会使用 MPX 运行时,因此 BNDSTX/BNDLDX 引用的边界表将不存在。
    猜你喜欢
    • 2013-10-17
    • 1970-01-01
    • 2017-10-16
    • 2015-06-30
    • 2011-05-24
    • 1970-01-01
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多