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 RDSEED 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 fa26647
Showing 1 changed file with 102 additions and 37 deletions.
139 changes: 102 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 RDSEED 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 : hasRdseed;
if (hasRdseed)
{
uint result;
asm @nogc nothrow
{
db 0x0f, 0xc7, 0xf8; // rdseed EAX
jnc LnotUsingRdseed;
mov result, EAX;
}
return result;
}
LnotUsingRdseed:
}
return cast(uint) fallbackSeed();
}
}

Expand Down Expand Up @@ -1741,48 +1800,54 @@ 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
version (InlineAsm_X86_Any)
{
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))
import core.cpuid : hasRdseed;
if (hasRdseed)
{
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;
static if (UIntType.sizeof <= uint.sizeof)
{
uint result;
asm @nogc nothrow
{
db 0x0f, 0xc7, 0xf8; // rdseed EAX
jnc LnotUsingRdseed;
mov result, EAX;
}
return cast(UIntType) result;
}
else version (D_InlineAsm_X86_64)
{
ulong result;
asm @nogc nothrow
{
db 0x48, 0x0f, 0xc7, 0xf8; // rdseed RAX
jnc LnotUsingRdseed;
mov result, RAX;
}
return result;
}
else
{
uint resultLow, resultHigh;
asm @nogc nothrow
{
db 0x0f, 0xc7, 0xf8; // rdseed EAX
jnc LnotUsingRdseed;
db 0x0f, 0xc7, 0xfb; // rdseed EBX
jnc LnotUsingRdseed;
mov resultLow, EAX;
mov resultHigh, EBX;
}
return ((cast(ulong) resultHigh) << 32) ^ resultLow;
}

}
return fmix64(seed += gamma);
LnotUsingRdseed:
}
return cast(UIntType) fallbackSeed();
}
}
}
Expand Down

0 comments on commit fa26647

Please sign in to comment.