Skip to content

Commit

Permalink
Fix Issue 20723 - std.random.unpredictableSeed: on x86/x86-64 try usi…
Browse files Browse the repository at this point in the history
…ng RDRAND when there is no arc4random

This code works in both DMD and LDC because LDC version 1.20.0
added support for data directives in DMD-style inline asm.
  • Loading branch information
n8sh committed Apr 7, 2020
1 parent cd2b755 commit 8950926
Showing 1 changed file with 101 additions and 37 deletions.
138 changes: 101 additions & 37 deletions std/random.d
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ else version (TVOS)
else version (WatchOS)
version = Darwin;

version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;

///
@safe unittest
{
Expand Down Expand Up @@ -1688,6 +1691,46 @@ else
result = (result ^ (result >>> 47)) * m;
return result ^ (result >>> 47);
}

// If we don't have arc4random and we don't have RDRAND fall back to this.
private ulong fallbackSeed() @nogc nothrow
{
// Bit avalanche function from MurmurHash3.
static ulong fmix64(ulong k) @nogc nothrow pure @safe
{
k = (k ^ (k >>> 33)) * 0xff51afd7ed558ccd;
k = (k ^ (k >>> 33)) * 0xc4ceb9fe1a85ec53;
return k ^ (k >>> 33);
}
// Using SplitMix algorithm with constant gamma.
// Chosen gamma is the odd number closest to 2^^64
// divided by the silver ratio (1.0L + sqrt(2.0L)).
enum gamma = 0x6a09e667f3bcc909UL;
import core.atomic : has64BitCAS;
static if (has64BitCAS)
{
import core.atomic : MemoryOrder, atomicLoad, atomicOp, atomicStore, cas;
shared static ulong seed;
shared static bool initialized;
if (0 == atomicLoad!(MemoryOrder.raw)(initialized))
{
cas(&seed, 0UL, fmix64(bootstrapSeed()));
atomicStore!(MemoryOrder.rel)(initialized, true);
}
return fmix64(atomicOp!"+="(seed, gamma));
}
else
{
static ulong seed;
static bool initialized;
if (!initialized)
{
seed = fmix64(bootstrapSeed());
initialized = true;
}
return fmix64(seed += gamma);
}
}
}

/**
Expand All @@ -1712,7 +1755,23 @@ how excellent the source of entropy is.
}
else
{
return cast(uint) unpredictableSeed!ulong;
version (InlineAsm_X86_Any)
{
import core.cpuid : hasRdrand;
if (hasRdrand)
{
uint result;
asm @nogc nothrow
{
db 0x0f, 0xc7, 0xf0; // rdrand EAX
jnc LnotUsingRdrand;
mov result, EAX;
}
return result;
}
LnotUsingRdrand:
}
return cast(uint) fallbackSeed();
}
}

Expand Down Expand Up @@ -1741,48 +1800,53 @@ if (isUnsigned!UIntType)
return result;
}
}
else static if (!is(UIntType == ulong))
{
// The work occurs in the ulong implementation.
return cast(UIntType) unpredictableSeed!ulong;
}
else
{
// Bit avalanche function from MurmurHash3.
static ulong fmix64(ulong k) @nogc nothrow pure @safe
{
k = (k ^ (k >>> 33)) * 0xff51afd7ed558ccd;
k = (k ^ (k >>> 33)) * 0xc4ceb9fe1a85ec53;
return k ^ (k >>> 33);
}
// Using SplitMix algorithm with constant gamma.
// Chosen gamma is the odd number closest to 2^^64
// divided by the silver ratio (1.0L + sqrt(2.0L)).
enum gamma = 0x6a09e667f3bcc909UL;
import core.atomic : has64BitCAS;
static if (has64BitCAS)
{
import core.atomic : MemoryOrder, atomicLoad, atomicOp, atomicStore, cas;
shared static ulong seed;
shared static bool initialized;
if (0 == atomicLoad!(MemoryOrder.raw)(initialized))
{
cas(&seed, 0UL, fmix64(bootstrapSeed()));
atomicStore!(MemoryOrder.rel)(initialized, true);
}
return fmix64(atomicOp!"+="(seed, gamma));
}
else
version (InlineAsm_X86_Any)
{
static ulong seed;
static bool initialized;
if (!initialized)
import core.cpuid : hasRdrand;
if (hasRdrand)
{
seed = fmix64(bootstrapSeed());
initialized = true;
static if (UIntType.sizeof <= uint.sizeof)
{
uint result;
asm @nogc nothrow
{
db 0x0f, 0xc7, 0xf0; // rdrand EAX
jnc LnotUsingRdrand;
mov result, EAX;
}
return cast(UIntType) result;
}
else version (D_InlineAsm_X86_64)
{
ulong result;
asm @nogc nothrow
{
db 0x48, 0x0f, 0xc7, 0xf0; // rdrand RAX
jnc LnotUsingRdrand;
mov result, RAX;
}
return result;
}
else
{
uint resultLow, resultHigh;
asm @nogc nothrow
{
db 0x0f, 0xc7, 0xf0; // rdrand EAX
jnc LnotUsingRdrand;
mov resultLow, EAX;
db 0x0f, 0xc7, 0xf3; // rdrand EBX
jnc LnotUsingRdrand;
mov resultHigh, EBX;
}
return ((cast(ulong) resultHigh) << 32) ^ resultLow;
}
}
return fmix64(seed += gamma);
LnotUsingRdrand:
}
return cast(UIntType) fallbackSeed();
}
}
}
Expand Down

0 comments on commit 8950926

Please sign in to comment.