From 6db4ff55bee893cac33ee7ed5ef057127a957231 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 30 Dec 2021 10:30:57 -0500 Subject: [PATCH] Seed Libc rng from system entropy 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. --- base/Base.jl | 3 ++ base/libc.jl | 2 +- base/randomdevice.jl | 77 +++++++++++++++++++++++++++++++++++++++ stdlib/Random/src/RNGs.jl | 41 ++------------------- 4 files changed, 85 insertions(+), 38 deletions(-) create mode 100644 base/randomdevice.jl diff --git a/base/Base.jl b/base/Base.jl index 91094f94ec213..bccd8e4e25e78 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -290,6 +290,9 @@ include("process.jl") include("ttyhascolor.jl") include("secretbuffer.jl") +# RandomDevice support +include("randomdevice.jl") + # core math functions include("floatfuncs.jl") include("math.jl") diff --git a/base/libc.jl b/base/libc.jl index 8cce4ce2a259b..38b62847eaeb4 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -400,7 +400,7 @@ rand(::Type{Float64}) = rand(UInt32) * 2.0^-32 Interface to the C `srand(seed)` function. """ -srand(seed=floor(Int, time()) % Cuint) = ccall(:srand, Cvoid, (Cuint,), seed) +srand(seed=Base._make_uint_seed()) = ccall(:srand, Cvoid, (Cuint,), seed) struct Cpasswd username::Cstring diff --git a/base/randomdevice.jl b/base/randomdevice.jl new file mode 100644 index 0000000000000..d63ff7edc1647 --- /dev/null +++ b/base/randomdevice.jl @@ -0,0 +1,77 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# This file contains the minimal support of RandomDevice for Base's own usage. +# The actual RandomDevice type that makes use of this infrastructure is defined +# in the Random stdlib. + +module DevRandomState + if !Sys.iswindows() + mutable struct FileRef + @atomic file::Union{IOStream, Nothing} + end + const DEV_RANDOM = FileRef(nothing) + const DEV_URANDOM = FileRef(nothing) + end + function __init__() + if !Sys.iswindows() + @atomic DEV_RANDOM.file = nothing + @atomic DEV_URANDOM.file = nothing + end + end +end + +if Sys.iswindows() + function RtlGenRandom!(A::Union{Array, Ref}) + Base.windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall( + (:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32), + A, sizeof(A))) + end + + # Manually implemented to work without the Random machinery + function _rand_uint() + r = Ref{Cuint}() + RtlGenRandom!(r) + return r[] + end +else # !windows + function _get_dev_random_fd(unlimited::Bool) + ref = unlimited ? DevRandomState.DEV_URANDOM : DevRandomState.DEV_RANDOM + fd = ref.file + if fd === nothing + fd = open(unlimited ? "/dev/urandom" : "/dev/random") + old, ok = @atomicreplace ref.file nothing => fd + if !ok + close(fd) + fd = old::IOStream + end + end + return fd + end + + # Manually implemented to work without the Random machinery + function _rand_uint() + return read(_get_dev_random_fd(true), Cuint) + end +end # os-test + +function _ad_hoc_entropy() + println(stderr, + "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") + seed = reinterpret(UInt64, time()) + seed = hash(seed, getpid() % UInt) + try + seed = hash(seed, parse(UInt64, + read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], + base = 16) % UInt) + catch + end + return seed +end + +function _make_uint_seed() + try + _rand_uint() + catch + return _ad_hoc_entropy() % Cuint + end +end \ No newline at end of file diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 13e5a8c778b16..a50f633e68a9c 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -23,42 +23,20 @@ else # !windows RandomDevice(; unlimited::Bool=true) = new(unlimited) end + getfile(rd::RandomDevice) = Base._get_dev_random_fd(rd.unlimited) + rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = read(getfile(rd), sp[]) rand(rd::RandomDevice, ::SamplerType{Bool}) = read(getfile(rd), UInt8) % Bool - mutable struct FileRef - @atomic file::Union{IOStream, Nothing} - end - - const DEV_RANDOM = FileRef(nothing) - const DEV_URANDOM = FileRef(nothing) - - function getfile(rd::RandomDevice) - ref = rd.unlimited ? DEV_URANDOM : DEV_RANDOM - fd = ref.file - if fd === nothing - fd = open(rd.unlimited ? "/dev/urandom" : "/dev/random") - old, ok = @atomicreplace ref.file nothing => fd - if !ok - close(fd) - fd = old::IOStream - end - end - return fd - end - show(io::IO, rd::RandomDevice) = print(io, RandomDevice, rd.unlimited ? "()" : "(unlimited=false)") - end # os-test # NOTE: this can't be put within the if-else block above for T in (Bool, BitInteger_types...) if Sys.iswindows() @eval function rand!(rd::RandomDevice, A::Array{$T}, ::SamplerType{$T}) - Base.windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall( - (:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32), - A, sizeof(A))) + Base.RtlGenRandom!(A) A end else @@ -332,14 +310,7 @@ function make_seed() catch println(stderr, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") - seed = reinterpret(UInt64, time()) - seed = hash(seed, getpid() % UInt) - try - seed = hash(seed, parse(UInt64, - read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], - base = 16) % UInt) - catch - end + Base._ad_hoc_entropy_source() return make_seed(seed) end end @@ -428,10 +399,6 @@ for T in BitInteger_types end function __init__() - @static if !Sys.iswindows() - @atomic DEV_RANDOM.file = nothing - @atomic DEV_URANDOM.file = nothing - end seed!(GLOBAL_RNG) end