【问题标题】:OPENSSL_ia32_rdrand cmp/cmove instructionsOPENSSL_ia32_rdrand cmp/cmove 指令
【发布时间】:2014-06-15 03:45:47
【问题描述】:

我试图了解 OpenSSL 中使用的 x86 汇编函数,以使用 CPU 的 RDRAND 指令获取随机字节。这是函数体:

OPENSSL_ia32_rdrand:
    mov     $8,%ecx
.Loop_rdrand:
    rdrand  %rax
    jc      .Lbreak_rdrand
    loop    .Loop_rdrand
.Lbreak_rdrand:
    cmp     $0,%rax
    cmove   %rcx,%rax
    ret

我无法弄清楚 cmp 和 cmove 指令的用途。我不是 x86 汇编程序员,但从我看过的参考资料中可以看出,我对它在做什么的理解是:

10 load retry counter to ecx
20 get random number into rax
30 if successful (carry flag set) goto 50
40 decrement counter and goto 20 if not zero
50 check if rax is zero
60 if it is, move rcx to rax
70 return 

因此,如果 rdrand 失败(进位标志清零),rax 只能为 0,并且只有在 1) rdrand 成功(进位集)并且在 rax 中放入非零数或 2 时才能到达 cmp 指令) ecx 中的重试计数器已减为 0。在情况 1 中,cmp 失败并且 cmove 不执行移动。在情况 2 中,rcx 包含 0,cmove 将 0 移动到 0。无论哪种方式,cmove 都是空操作。

我是否误解了一个或多个汇编指令的作用?

【问题讨论】:

  • 好问题和好问题。 OpenSSL 项目有一个非常敏锐的家伙会做这些事情,所以看看他的推理会很有趣。

标签: assembly x86 openssl


【解决方案1】:

不只是你有轻微的误会。编写此程序的人不知道他们在做什么(嗯,您说的是 OpenSSL……真是令人惊讶)。英特尔的软件开发者手册推荐:

#define SUCCESS 1
#define RETRY_LIMIT_EXCEEDED 0
#define RETRY_LIMIT 10

int get_random_64( unsigned __int 64 * arand)
{
int i ;
for ( i = 0; i < RETRY_LIMIT; i ++)
    {
        if(_rdrand64_step(arand) ) return SUCCESS;
    }
return RETRY_LIMIT_EXCEEDED;
}

经过一些小改动让它在 gcc 上编译后,我得到:

get_random_64:
    movl    $10, %edx
    movl    $1, %ecx

.L3:
    rdrand  %rax
    movq    %rax, (%rdi)
    cmovb   %ecx, %eax
    testl   %eax, %eax
    jne .L4
    subq    $1, %rdx
    jne .L3
    rep ret
.L4:
    movl    $1, %eax
    ret

rdrand() 随机生成它时,这实际上将返回一个零,而不是在这种情况下返回一个小的非零整数。我想说一个不能返回 0 并且返回 1 到 8 之间整数的概率略有增加的随机函数在设计上是有缺陷的,因为它的输出是有偏差的。

【讨论】:

  • 好吧,代码可能试图避免灾难性故障,以便不将全 0 的字符串返回给调用者。但即使是[1 -8] 范围内的值,对我来说似乎也是一场灾难性的失败。很难说,因为没有 cmets 来解释推理。很清楚:API 似乎已损坏,因为它无法传达成功/失败。调用者总是会像成功一样继续前进。
  • 公平地说,OPENSSL_ia32_rdrand 可以通过返回 0 来传达失败。它只是不能返回随机 0,我同意这是一个损坏的 API。
  • 哦,好的。所以RAX是64位,这意味着函数可以返回2^64 - 1值(而不是完整的2^64)。我无法想象对手会从中获得优势。如果代码被这样评论就好了。
  • @user128469 好吧,在 openSSL 版本中返回零的最可能情况是硬件缺陷,在这种情况下,函数返回的 all 数字将为零。我没有印象。我同意不返回随机零并不是一个严重的问题,但是 1-8 的偏差是
【解决方案2】:

你有点误会了。

您的主要部分是正确的,有 8 次尝试使用 RDRAND 检索随机数,但如果 RDRAND 返回 0 作为 随机数CMOVE 不一定是 NOP。在这种情况下,此函数将返回 RCX(循环计数)的值作为“随机数”。简而言之,此函数仅在没有可用随机数时返回 0 - 否则它将返回来自 RDRAND 的真实随机数或从 RDRAND 收到 0 的迭代数。

【讨论】:

  • 我认为这是不可能的。我正在查看的英特尔参考资料指出,当 CF=1 时,“目标寄存器有效。非零随机值在执行时可用。” (强调我的。)在做了更多的研究之后,它似乎只适用于早期版本的 CPU。当前的确实可以返回一个随机零,因为它们应该。我怀疑返回重试计数器而不是 0 的决定是因为 OpenSSL 的 API 已经建立以使用它来发出错误信号,但我只是在推测。尽管如此,不能返回 0 的随机数函数似乎完全是错误的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-20
  • 1970-01-01
  • 2021-03-08
  • 1970-01-01
  • 1970-01-01
  • 2012-01-12
相关资源
最近更新 更多