Skip to content

Instantly share code, notes, and snippets.

@edwintorok
Created January 11, 2021 23:13
Show Gist options
  • Save edwintorok/190ccbccbbd8454ca4b060bd82dec7f1 to your computer and use it in GitHub Desktop.
Save edwintorok/190ccbccbbd8454ca4b060bd82dec7f1 to your computer and use it in GitHub Desktop.
gcc rdrand.c -O2 -o rdrand -fopenmp -Wall
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#if defined(__i386__) || defined(__x86_64__)
#include <cpuid.h>
#endif
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/* from systemd random-util.c: */
static int detect_rdrand()
{
bool have_rdrand;
uint32_t eax, ebx, ecx, edx;
/* Check if RDRAND is supported by the CPU */
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) {
have_rdrand = false;
return -EOPNOTSUPP;
}
/* Compat with old gcc where bit_RDRND didn't exist yet */
#ifndef bit_RDRND
#define bit_RDRND (1U << 30)
#endif
have_rdrand = !!(ecx & bit_RDRND);
#if 0
if (have_rdrand > 0) {
/* Allow disabling use of RDRAND with SYSTEMD_RDRAND=0
If it is unset getenv_bool_secure will return a negative value. */
if (getenv_bool_secure("SYSTEMD_RDRAND") == 0) {
have_rdrand = false;
return -EOPNOTSUPP;
}
}
#endif
if (have_rdrand == 0)
return -EOPNOTSUPP;
return 0;
}
static inline int rdrand(unsigned long *ret) {
#if defined(__i386__) || defined(__x86_64__)
unsigned long v;
uint8_t success;
asm volatile("rdrand %0;"
"setc %1"
: "=r" (v),
"=qm" (success));
#if 0
msan_unpoison(&success, sizeof(success));
#endif
if (!success)
return -EAGAIN;
/* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success
* via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be
* a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking
* for this special value (and also 0, just to be sure) and filtering it out. This is a work-around
* only however and something AMD really should fix properly. The Linux kernel should probably work
* around this issue by turning off RDRAND altogether on those CPUs. See:
* https://github.com/systemd/systemd/issues/11810 */
if (v == 0 || v == ULONG_MAX)
#if 0
return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
"RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v);
#else
{
fprintf(stderr, "RDRAND returned suspicious value %lx, assuming bad hardware RNG\n", v);
return EUCLEAN;
}
#endif
*ret = v;
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int main(int argc, char *argv[])
{
unsigned long val[32*1024];
errno = -detect_rdrand();
if (errno) {
perror("rdrand cpuid");
exit(1);
}
do {
#pragma omp parallel for
for(int i=0;i < sizeof(val)/sizeof(*val);i+=8) {
/* errno = -rdrand(&val[i]);
if (errno) {
perror("rdrand");
exit(2);
}
*/
rdrand(&val[i]);
rdrand(&val[i+1]);
rdrand(&val[i+2]);
rdrand(&val[i+3]);
rdrand(&val[i+4]);
rdrand(&val[i+5]);
rdrand(&val[i+6]);
rdrand(&val[i+7]);
}
fwrite(val, sizeof(val), 1, stdout);
} while(1);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment