Skip to content

Commit 5819775

Browse files
authored
Seed Libc rng from system entropy (#43606)
We're seeing lots of CI failures that look like collisions in the names of temporary variables. Currently we're seeding the libc rng from a low entropy clock, which is somewhat likely to have collisions particular if (as we are), you launch all your processes at exactly the right time. Try to fix that by seeding the libc using system entropy. Of course that still leaves the potential for birthday problems, but hopfully this will tide us over until #43597 gets fixed.
1 parent 28b47b8 commit 5819775

File tree

4 files changed

+85
-38
lines changed

4 files changed

+85
-38
lines changed

base/Base.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ include("process.jl")
290290
include("ttyhascolor.jl")
291291
include("secretbuffer.jl")
292292

293+
# RandomDevice support
294+
include("randomdevice.jl")
295+
293296
# core math functions
294297
include("floatfuncs.jl")
295298
include("math.jl")

base/libc.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ rand(::Type{Float64}) = rand(UInt32) * 2.0^-32
400400
401401
Interface to the C `srand(seed)` function.
402402
"""
403-
srand(seed=floor(Int, time()) % Cuint) = ccall(:srand, Cvoid, (Cuint,), seed)
403+
srand(seed=Base._make_uint_seed()) = ccall(:srand, Cvoid, (Cuint,), seed)
404404

405405
struct Cpasswd
406406
username::Cstring

base/randomdevice.jl

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
# This file contains the minimal support of RandomDevice for Base's own usage.
4+
# The actual RandomDevice type that makes use of this infrastructure is defined
5+
# in the Random stdlib.
6+
7+
module DevRandomState
8+
if !Sys.iswindows()
9+
mutable struct FileRef
10+
@atomic file::Union{IOStream, Nothing}
11+
end
12+
const DEV_RANDOM = FileRef(nothing)
13+
const DEV_URANDOM = FileRef(nothing)
14+
end
15+
function __init__()
16+
if !Sys.iswindows()
17+
@atomic DEV_RANDOM.file = nothing
18+
@atomic DEV_URANDOM.file = nothing
19+
end
20+
end
21+
end
22+
23+
if Sys.iswindows()
24+
function RtlGenRandom!(A::Union{Array, Ref})
25+
Base.windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall(
26+
(:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32),
27+
A, sizeof(A)))
28+
end
29+
30+
# Manually implemented to work without the Random machinery
31+
function _rand_uint()
32+
r = Ref{Cuint}()
33+
RtlGenRandom!(r)
34+
return r[]
35+
end
36+
else # !windows
37+
function _get_dev_random_fd(unlimited::Bool)
38+
ref = unlimited ? DevRandomState.DEV_URANDOM : DevRandomState.DEV_RANDOM
39+
fd = ref.file
40+
if fd === nothing
41+
fd = open(unlimited ? "/dev/urandom" : "/dev/random")
42+
old, ok = @atomicreplace ref.file nothing => fd
43+
if !ok
44+
close(fd)
45+
fd = old::IOStream
46+
end
47+
end
48+
return fd
49+
end
50+
51+
# Manually implemented to work without the Random machinery
52+
function _rand_uint()
53+
return read(_get_dev_random_fd(true), Cuint)
54+
end
55+
end # os-test
56+
57+
function _ad_hoc_entropy()
58+
println(stderr,
59+
"Entropy pool not available to seed RNG; using ad-hoc entropy sources.")
60+
seed = reinterpret(UInt64, time())
61+
seed = hash(seed, getpid() % UInt)
62+
try
63+
seed = hash(seed, parse(UInt64,
64+
read(pipeline(`ifconfig`, `sha1sum`), String)[1:40],
65+
base = 16) % UInt)
66+
catch
67+
end
68+
return seed
69+
end
70+
71+
function _make_uint_seed()
72+
try
73+
_rand_uint()
74+
catch
75+
return _ad_hoc_entropy() % Cuint
76+
end
77+
end

stdlib/Random/src/RNGs.jl

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,42 +23,20 @@ else # !windows
2323
RandomDevice(; unlimited::Bool=true) = new(unlimited)
2424
end
2525

26+
getfile(rd::RandomDevice) = Base._get_dev_random_fd(rd.unlimited)
27+
2628
rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = read(getfile(rd), sp[])
2729
rand(rd::RandomDevice, ::SamplerType{Bool}) = read(getfile(rd), UInt8) % Bool
2830

29-
mutable struct FileRef
30-
@atomic file::Union{IOStream, Nothing}
31-
end
32-
33-
const DEV_RANDOM = FileRef(nothing)
34-
const DEV_URANDOM = FileRef(nothing)
35-
36-
function getfile(rd::RandomDevice)
37-
ref = rd.unlimited ? DEV_URANDOM : DEV_RANDOM
38-
fd = ref.file
39-
if fd === nothing
40-
fd = open(rd.unlimited ? "/dev/urandom" : "/dev/random")
41-
old, ok = @atomicreplace ref.file nothing => fd
42-
if !ok
43-
close(fd)
44-
fd = old::IOStream
45-
end
46-
end
47-
return fd
48-
end
49-
5031
show(io::IO, rd::RandomDevice) =
5132
print(io, RandomDevice, rd.unlimited ? "()" : "(unlimited=false)")
52-
5333
end # os-test
5434

5535
# NOTE: this can't be put within the if-else block above
5636
for T in (Bool, BitInteger_types...)
5737
if Sys.iswindows()
5838
@eval function rand!(rd::RandomDevice, A::Array{$T}, ::SamplerType{$T})
59-
Base.windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall(
60-
(:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32),
61-
A, sizeof(A)))
39+
Base.RtlGenRandom!(A)
6240
A
6341
end
6442
else
@@ -332,14 +310,7 @@ function make_seed()
332310
catch
333311
println(stderr,
334312
"Entropy pool not available to seed RNG; using ad-hoc entropy sources.")
335-
seed = reinterpret(UInt64, time())
336-
seed = hash(seed, getpid() % UInt)
337-
try
338-
seed = hash(seed, parse(UInt64,
339-
read(pipeline(`ifconfig`, `sha1sum`), String)[1:40],
340-
base = 16) % UInt)
341-
catch
342-
end
313+
Base._ad_hoc_entropy_source()
343314
return make_seed(seed)
344315
end
345316
end
@@ -428,10 +399,6 @@ for T in BitInteger_types
428399
end
429400

430401
function __init__()
431-
@static if !Sys.iswindows()
432-
@atomic DEV_RANDOM.file = nothing
433-
@atomic DEV_URANDOM.file = nothing
434-
end
435402
seed!(GLOBAL_RNG)
436403
end
437404

0 commit comments

Comments
 (0)