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

std.random.unpredictableSeed: on x86/x86-64 try using RDRAND when there is no arc4random #7436

Merged
merged 1 commit into from
Apr 16, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
154 changes: 117 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,28 @@ 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;
// Some AMD CPUs shipped with bugs where RDRAND could fail
// but still set the carry flag to 1. In those cases the
// output will be -1.
cmp EAX, 0xffff_ffff;
je LnotUsingRdrand;
mov result, EAX;
}
return result;
}
LnotUsingRdrand:
}
return cast(uint) fallbackSeed();
}
}

Expand Down Expand Up @@ -1741,48 +1805,64 @@ 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;
// Some AMD CPUs shipped with bugs where RDRAND could fail
// but still set the carry flag to 1. In those cases the
// output will be -1.
cmp EAX, 0xffff_ffff;
je 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;
// Some AMD CPUs shipped with bugs where RDRAND could fail
// but still set the carry flag to 1. In those cases the
// output will be -1.
cmp RAX, 0xffff_ffff_ffff_ffff;
je 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, 0xf0; // rdrand EAX
jnc LnotUsingRdrand;
mov resultHigh, EAX;
}
if (resultLow != uint.max || resultHigh != uint.max) // Protect against AMD RDRAND bug.
return ((cast(ulong) resultHigh) << 32) ^ resultLow;
}
}
return fmix64(seed += gamma);
LnotUsingRdrand:
}
return cast(UIntType) fallbackSeed();
}
}
}
Expand Down