Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add check for broken Ryzen RDRAND #67

Merged
merged 6 commits into from
Nov 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Changelog

## 1.2.0
## 1.2.0 (Unreleased)

### Improvements
* Now uses [OpenSSL 1.1.1.d](https://www.openssl.org/source/openssl-1.1.1d.tar.gz)

### Patches
* Detects stuck AMD Ryzen RDRAND and correctly treats as an error

### Maintenance
* Add prefix to test output lines indicating if suite will fail.
* Now colored output from tests can be disabled by setting the environment variable `ACCP_TEST_COLOR` to `false`
Expand Down
4 changes: 4 additions & 0 deletions csrc/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
#include <openssl/err.h>
#endif

#ifndef UINT64_MAX
#define UINT64_MAX (~((uint64_t) 0))
#endif

namespace AmazonCorrettoCryptoProvider {
void capture_trace(std::vector<void *> &trace) COLD;
void format_trace(std::ostringstream &, const std::vector<void *> &trace) COLD;
Expand Down
31 changes: 27 additions & 4 deletions csrc/rdrand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ using namespace AmazonCorrettoCryptoProvider;

namespace AmazonCorrettoCryptoProvider {

// Some inline machine-code which cannot always be represented
// by standard inline assembly mnemonics.

// rdrand %rcx
#define ASM_RDRAND_RCX ".byte 0x48, 0x0f, 0xc7, 0xf1\n"
// rdseed %rcx
#define ASM_RDSEED_RCX ".byte 0x48, 0x0f, 0xc7, 0xf9\n"
// PAUSE instruction. REP NOP
#define ASM_REP_NOP ".byte 0xf3, 0x90\n"

#define CPUID_PROBE_DONE (1 << 0)
#define CPUID_HAS_RDRAND (1 << 1)
#define CPUID_HAS_RDSEED (1 << 2)
Expand Down Expand Up @@ -111,7 +121,7 @@ bool rng_rdrand(uint64_t *out) {

bool success = 0;
__asm__ __volatile__(
".byte 0x48, 0x0f, 0xc7, 0xf1\n" // RDRAND %rcx
ASM_RDRAND_RCX
"setc %%al\n" // rax = 1 if success, 0 if fail
: "=c" (*out), "=a" (success)
: "c" (0), "a" (0)
Expand All @@ -134,7 +144,7 @@ bool rng_rdseed(uint64_t *out) {

bool success;
__asm__ __volatile__(
".byte 0x48, 0x0f, 0xc7, 0xf9\n" // RDSEED %rcx
ASM_RDSEED_RCX
"setc %%al\n" // rax = 1 if success, 0 if fail
: "=c" (*out), "=a" (success)
: "c" (0), "a" (0)
Expand All @@ -154,7 +164,7 @@ void pause_and_decrement(int &counter) {
//
// Unfortunately our ancient compilers don't like the PAUSE instruction - even when entered
// as "rep nop", so we need to use raw machine code here as well.
".byte 0xf3, 0x90\n" // PAUSE instruction (REP NOP)
ASM_REP_NOP // PAUSE instruction
"dec %0" // decrement retry counter
// prevent loop unrolling by hiding the loop decrement from the compiler
: "+r" (counter)
Expand Down Expand Up @@ -271,7 +281,20 @@ bool rng_retry_rdrand(uint64_t *dest) {

do {
if (likely(rng_rdrand(dest))) {
return true;
// Some AMD CPUs will find that RDRAND "sticks" on all 1s but still reports success.
// Some other very old CPUs use all 0s as an error condition while still reporting success.
// If we encounter either of these suspicious values (a 1/2^63 chance) we'll treat them as
// a failure and generate a new value.
//
// In the future we could add CPUID checks to detect processors with these known bugs,
// however it does not appear worth it. The entropy loss is negligible and the
// corresponding likelihood that a healthy CPU generates either of these values is also
// negligible (1/2^63). Finally, adding processor specific logic would greatly
// increase the complexity and would cause us to "miss" any unknown processors with
// similar bugs.
if (likely(*dest != UINT64_MAX && *dest != 0)) {
return true;
}
}

pause_and_decrement(tries);
Expand Down
31 changes: 31 additions & 0 deletions csrc/test_rdrand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#undef NDEBUG

#include "env.h"
#include "rdrand.h"
#include "config.h"
#include <stdlib.h>
Expand Down Expand Up @@ -40,6 +41,16 @@ bool rng_alternating(uint64_t *out) {
return true;
}

bool rng_stuck_zero(uint64_t *out) {
*out = 0;
return true;
}

bool rng_stuck_ff(uint64_t *out) {
*out = UINT64_MAX;
return true;
}

bool test_succeeded = true;

#define TEST_ASSERT(x) do { \
Expand Down Expand Up @@ -196,6 +207,25 @@ void when_rdseed_broken_rdrand_reduction_used() {
TEST_ASSERT(!memcmp(expected, buf, sizeof(buf)));
}

void when_rdrand_stuck_failure_returned() {
static const uint8_t expected[32] = { 0 };
static uint8_t buf[32];

hook_rdrand = rng_stuck_zero;

memset(buf, 1, sizeof(buf));

TEST_ASSERT(!rdrand(buf, sizeof(buf)));
TEST_ASSERT(!memcmp(expected, buf, sizeof(buf)));

memset(buf, 1, sizeof(buf));

hook_rdrand = rng_stuck_ff;

TEST_ASSERT(!rdrand(buf, sizeof(buf)));
TEST_ASSERT(!memcmp(expected, buf, sizeof(buf)));
}

} // anon namespace

#define RUNTEST(name) do { \
Expand All @@ -220,6 +250,7 @@ int main() {
RUNTEST(when_rng_fails_late_buffer_is_still_cleared);
RUNTEST(when_rdrand_broken_rdseed_works_eventually);
RUNTEST(when_rdseed_broken_rdrand_reduction_used);
RUNTEST(when_rdrand_stuck_failure_returned);

return success ? 0 : 1;
}
Expand Down