Skip to content

Commit

Permalink
[Support,Windows] Tolerate failure of CryptGenRandom
Browse files Browse the repository at this point in the history
Summary:
In `Unix/Process.inc`, we seed a random number generator from
`/dev/urandom` if possible, but if not, we're happy to fall back to
ordinary pseudorandom strategies, like the current time and PID.

The corresponding function on Windows calls `CryptGenRandom`, but it
//doesn't// have a fallback if that strategy fails. But `CryptGenRandom`
//can// fail, if a cryptography provider isn't properly initialized, or
occasionally (by our observation) simply intermittently.

If it's reasonable on Unix to implement traditional pseudorandom-number
seeding as a fallback, then it's surely reasonable to do the same on
Windows. So this patch adds a last-ditch use of ordinary rand(), using
much the same strategy as the Unix fallback code.

Reviewers: hans, sammccall

Reviewed By: hans

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D77553
  • Loading branch information
statham-arm committed Apr 7, 2020
1 parent 4fc59a4 commit aab9e9d
Showing 1 changed file with 30 additions and 10 deletions.
40 changes: 30 additions & 10 deletions llvm/lib/Support/Windows/Process.inc
Expand Up @@ -439,18 +439,38 @@ const char *Process::ResetColor() {
return 0;
}

static unsigned GetRandomNumberSeed() {
// Generate a random number seed from the millisecond-resolution Windows
// system clock and the current process id.
FILETIME Time;
GetSystemTimeAsFileTime(&Time);
DWORD Pid = GetCurrentProcessId();
return hash_combine(Time.dwHighDateTime, Time.dwLowDateTime, Pid);
}

static unsigned GetPseudoRandomNumber() {
// Arrange to call srand once when this function is first used, and
// otherwise (if GetRandomNumber always succeeds in using
// CryptGenRandom) don't bother at all.
static int x = (static_cast<void>(::srand(GetRandomNumberSeed())), 0);
(void)x;
return ::rand();
}

unsigned Process::GetRandomNumber() {
// Try to use CryptGenRandom.
HCRYPTPROV HCPC;
if (!::CryptAcquireContextW(&HCPC, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
ReportLastErrorFatal("Could not acquire a cryptographic context");

ScopedCryptContext CryptoProvider(HCPC);
unsigned Ret;
if (!::CryptGenRandom(CryptoProvider, sizeof(Ret),
reinterpret_cast<BYTE *>(&Ret)))
ReportLastErrorFatal("Could not generate a random number");
return Ret;
if (::CryptAcquireContextW(&HCPC, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
ScopedCryptContext CryptoProvider(HCPC);
unsigned Ret;
if (::CryptGenRandom(CryptoProvider, sizeof(Ret),
reinterpret_cast<BYTE *>(&Ret)))
return Ret;
}

// If that fails, fall back to pseudo-random numbers.
return GetPseudoRandomNumber();
}

typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
Expand Down

0 comments on commit aab9e9d

Please sign in to comment.