Browse Source

Detect broken RDRAND during initialization.

Some CPUs advertise RDRAND in CPUID, but return
0xFFFFFFFF unconditionally.  To avoid locking up
later, test RDRAND during initialization, and if
it returns 0xFFFFFFFF, mark it as nonexistent.

Also fix a possible segmentation fault in CPUID check.

This commit is a squashed and slightly modified backport
of the following commits on the master branch:

  * 0e5bbcaa16
  * 4d36b0287d
  * 8086314026
pull/625/head
Björn Esser 5 years ago
parent
commit
a731ae6a75
1 changed files with 67 additions and 29 deletions
  1. +67
    -29
      random_seed.c

+ 67
- 29
random_seed.c View File

@@ -26,20 +26,11 @@

static void do_cpuid(int regs[], int h)
{
__asm__ __volatile__(
#if defined __x86_64__
"pushq %%rbx;\n"
#else
"pushl %%ebx;\n"
#endif
"cpuid;\n"
#if defined __x86_64__
"popq %%rbx;\n"
#else
"popl %%ebx;\n"
#endif
: "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
/* clang-format off */
__asm__ __volatile__("cpuid"
: "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
: "a"(h));
/* clang-format on */
}

#elif defined _MSC_VER
@@ -53,12 +44,51 @@ static void do_cpuid(int regs[], int h)

#if HAS_X86_CPUID

static int has_rdrand()
static int get_rdrand_seed(void);

/* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */
static int _has_rdrand = -1;

static int has_rdrand(void)
{
// CPUID.01H:ECX.RDRAND[bit 30] == 1
int regs[4];
do_cpuid(regs, 1);
return (regs[2] & (1 << 30)) != 0;
if (_has_rdrand != -1)
{
return _has_rdrand;
}

/* CPUID.01H:ECX.RDRAND[bit 30] == 1 */
int regs[4];
do_cpuid(regs, 1);
if (!(regs[2] & (1 << 30)))
{
_has_rdrand = 0;
return 0;
}

/*
* Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF
* unconditionally. To avoid locking up later, test RDRAND here. If over
* 3 trials RDRAND has returned the same value, declare it broken.
* Example CPUs are AMD Ryzen 3000 series
* and much older AMD APUs, such as the E1-1500
* https://github.com/systemd/systemd/issues/11810
* https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend
*/
_has_rdrand = 0;
int prev = get_rdrand_seed();
for (int i = 0; i < 3; i++)
{
int temp = get_rdrand_seed();
if (temp != prev)
{
_has_rdrand = 1;
break;
}

prev = temp;
}

return _has_rdrand;
}

#endif
@@ -69,17 +99,19 @@ static int has_rdrand()

#define HAVE_RDRAND 1

static int get_rdrand_seed()
static int get_rdrand_seed(void)
{
DEBUG_SEED("get_rdrand_seed");
int _eax;
// rdrand eax
__asm__ __volatile__("1: .byte 0x0F\n"
" .byte 0xC7\n"
" .byte 0xF0\n"
" jnc 1b;\n"
: "=a" (_eax));
return _eax;
DEBUG_SEED("get_rdrand_seed");
int _eax;
/* rdrand eax */
/* clang-format off */
__asm__ __volatile__("1: .byte 0x0F\n"
" .byte 0xC7\n"
" .byte 0xF0\n"
" jnc 1b;\n"
: "=a" (_eax));
/* clang-format on */
return _eax;
}

#endif
@@ -109,7 +141,7 @@ static int get_rdrand_seed()
DEBUG_SEED("get_rdrand_seed");
int _eax;
retry:
// rdrand eax
/* rdrand eax */
__asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0
__asm jnc retry
__asm mov _eax, eax
@@ -178,8 +210,14 @@ static int get_dev_random_seed()

#define HAVE_CRYPTGENRANDOM 1

/* clang-format off */
#include <windows.h>

/* Caution: these blank lines must remain so clang-format doesn't reorder
includes to put windows.h after wincrypt.h */

#include <wincrypt.h>
/* clang-format on */
#ifndef __GNUC__
#pragma comment(lib, "advapi32.lib")
#endif


Loading…
Cancel
Save