// random -*- C++ -*- // Copyright (C) 2012-2023 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // Under Section 7 of GPL version 3, you are granted additional // permissions described in the GCC Runtime Library Exception, version // 3.1, as published by the Free Software Foundation. // You should have received a copy of the GNU General Public License and // a copy of the GCC Runtime Library Exception along with this program; // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // . #define _GLIBCXX_USE_CXX11_ABI 1 #define _CRT_RAND_S // define this before including to get rand_s #include #include #if defined __i386__ || defined __x86_64__ # include # ifdef _GLIBCXX_X86_RDRAND # define USE_RDRAND 1 # endif # ifdef _GLIBCXX_X86_RDSEED # define USE_RDSEED 1 # endif #elif defined __powerpc64__ && defined __BUILTIN_CPU_SUPPORTS__ # define USE_DARN 1 #endif #include #include #include // For std::isdigit. #if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H # include # include // Use POSIX open, close, read etc. instead of ISO fopen, fclose, fread # define USE_POSIX_FILE_IO #endif #ifdef _GLIBCXX_HAVE_SYS_IOCTL_H # include #endif #ifdef _GLIBCXX_HAVE_LINUX_TYPES_H # include #endif #ifdef _GLIBCXX_HAVE_LINUX_RANDOM_H # include #endif #ifdef _GLIBCXX_USE_CRT_RAND_S # include #endif #ifdef _GLIBCXX_HAVE_GETENTROPY # include #endif #if defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM \ || _GLIBCXX_HAVE_GETENTROPY // The OS provides a source of randomness we can use. # pragma GCC poison _M_mt #elif defined USE_RDRAND || defined USE_RDSEED || defined USE_DARN // Hardware instructions might be available, but use cpuid checks at runtime. # pragma GCC poison _M_mt // If the runtime cpuid checks fail we'll use a linear congruential engine. # define USE_LCG 1 #else // Use the mt19937 member of the union, as in previous GCC releases. # define USE_MT19937 1 #endif #ifdef USE_LCG # include #endif namespace std _GLIBCXX_VISIBILITY(default) { namespace { [[noreturn]] inline void __throw_syserr([[maybe_unused]] int e, [[maybe_unused]] const char* msg) { _GLIBCXX_THROW_OR_ABORT(system_error(e, std::generic_category(), msg)); } #if USE_RDRAND unsigned int __attribute__ ((target("rdrnd"))) __x86_rdrand(void*) { unsigned int retries = 100; unsigned int val; while (__builtin_ia32_rdrand32_step(&val) == 0) [[__unlikely__]] if (--retries == 0) std::__throw_runtime_error(__N("random_device: rdrand failed")); return val; } #endif #if USE_RDSEED unsigned int __attribute__ ((target("rdseed"))) __x86_rdseed(void* fallback) { unsigned int retries = 100; unsigned int val; while (__builtin_ia32_rdseed_si_step(&val) == 0) [[__unlikely__]] { if (--retries == 0) { if (auto f = reinterpret_cast(fallback)) return f(nullptr); std::__throw_runtime_error(__N("random_device: rdseed failed")); } __builtin_ia32_pause(); } return val; } #if USE_RDRAND unsigned int __attribute__ ((target("rdseed,rdrnd"))) __x86_rdseed_rdrand(void*) { return __x86_rdseed(reinterpret_cast(&__x86_rdrand)); } #endif #endif #ifdef USE_DARN unsigned int __attribute__((target("cpu=power9"))) __ppc_darn(void*) { const uint64_t failed = -1; unsigned int retries = 10; uint64_t val = __builtin_darn(); while (val == failed) [[__unlikely__]] { if (--retries == 0) std::__throw_runtime_error(__N("random_device: darn failed")); val = __builtin_darn(); } return (uint32_t)val; } #endif #ifdef _GLIBCXX_USE_CRT_RAND_S unsigned int __winxp_rand_s(void*) { unsigned int val; if (::rand_s(&val) != 0) std::__throw_runtime_error(__N("random_device: rand_s failed")); return val; } #endif #ifdef _GLIBCXX_HAVE_GETENTROPY unsigned int __libc_getentropy(void*) { unsigned int val; if (::getentropy(&val, sizeof(val)) != 0) std::__throw_runtime_error(__N("random_device: getentropy failed")); return val; } #endif #ifdef _GLIBCXX_HAVE_ARC4RANDOM unsigned int __libc_arc4random(void*) { return ::arc4random(); } #endif #ifdef USE_LCG // TODO: use this to seed std::mt19937 engine too. unsigned bad_seed(void* p) noexcept { // Poor quality seed based on hash of the current time and the address // of the object being seeded. Better than using the same default seed // for every object though. const uint64_t bits[] = { (uint64_t) chrono::system_clock::now().time_since_epoch().count(), (uint64_t) reinterpret_cast(p) }; auto bytes = reinterpret_cast(bits); // 32-bit FNV-1a hash uint32_t h = 2166136261u; for (unsigned i = 0; i < sizeof(bits); ++i) { h ^= *bytes++; h *= 16777619u; } return h; } // Same as std::minstd_rand0 but using unsigned not uint_fast32_t. using lcg_type = linear_congruential_engine; inline lcg_type* construct_lcg_at(void* addr) noexcept { return ::new(addr) lcg_type(bad_seed(addr)); } inline void destroy_lcg_at(void* addr) noexcept { static_cast(addr)->~lcg_type(); } unsigned int __lcg(void* ptr) noexcept { auto& lcg = *static_cast(ptr); return lcg(); } #endif enum Which : unsigned { device_file = 1, prng = 2, rand_s = 4, getentropy = 8, arc4random = 16, rdseed = 64, rdrand = 128, darn = 256, any = 0xffff }; constexpr Which operator|(Which l, Which r) noexcept { return Which(unsigned(l) | unsigned(r)); } inline Which which_source(random_device::result_type (*func [[maybe_unused]])(void*), void* file [[maybe_unused]]) { #ifdef _GLIBCXX_USE_CRT_RAND_S if (func == &__winxp_rand_s) return rand_s; #endif #ifdef USE_RDSEED #ifdef USE_RDRAND if (func == &__x86_rdseed_rdrand) return rdseed; #endif if (func == &__x86_rdseed) return rdseed; #endif #ifdef USE_RDRAND if (func == &__x86_rdrand) return rdrand; #endif #ifdef USE_DARN if (func == &__ppc_darn) return darn; #endif #ifdef _GLIBCXX_USE_DEV_RANDOM if (file != nullptr) return device_file; #endif #ifdef _GLIBCXX_HAVE_ARC4RANDOM if (func == __libc_arc4random) return arc4random; #endif #ifdef _GLIBCXX_HAVE_GETENTROPY if (func == __libc_getentropy) return getentropy; #endif #ifdef USE_LCG if (func == &__lcg) return prng; #endif #ifdef USE_MT19937 return prng; #endif return any; // should be unreachable } } void random_device::_M_init(const std::string& token) { #ifdef USE_MT19937 // If no real random device is supported then use the mt19937 engine. _M_init_pretr1(token); return; #else _M_file = nullptr; _M_func = nullptr; _M_fd = -1; const char* fname [[gnu::unused]] = nullptr; Which which; if (token == "default") { which = any; fname = "/dev/urandom"; } #ifdef USE_RDSEED else if (token == "rdseed") which = rdseed; #endif // USE_RDSEED #ifdef USE_RDRAND else if (token == "rdrand" || token == "rdrnd") which = rdrand; #endif // USE_RDRAND #ifdef USE_DARN else if (token == "darn") which = darn; #endif #if defined USE_RDRAND || defined USE_RDSEED || defined USE_DARN else if (token == "hw" || token == "hardware") which = rdrand | rdseed | darn; #endif #ifdef _GLIBCXX_USE_CRT_RAND_S else if (token == "rand_s") which = rand_s; #endif // _GLIBCXX_USE_CRT_RAND_S #ifdef _GLIBCXX_HAVE_GETENTROPY else if (token == "getentropy") which = getentropy; #endif // _GLIBCXX_HAVE_GETENTROPY #ifdef _GLIBCXX_HAVE_ARC4RANDOM else if (token == "arc4random") which = arc4random; #endif // _GLIBCXX_HAVE_ARC4RANDOM #ifdef _GLIBCXX_USE_DEV_RANDOM else if (token == "/dev/urandom" || token == "/dev/random") { fname = token.c_str(); which = device_file; } #endif // _GLIBCXX_USE_DEV_RANDOM #ifdef USE_LCG else if (token == "prng") which = prng; #endif else std::__throw_syserr(EINVAL, __N("random_device::random_device" "(const std::string&):" " unsupported token")); #ifdef _GLIBCXX_USE_CRT_RAND_S if (which & rand_s) { _M_func = &__winxp_rand_s; return; } #endif // _GLIBCXX_USE_CRT_RAND_S #ifdef USE_RDSEED if (which & rdseed) { unsigned int eax, ebx, ecx, edx; // Check availability of cpuid and, for now at least, also the // CPU signature for Intel and AMD. if (__get_cpuid_max(0, &ebx) > 0 && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx)) { // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18] __cpuid_count(7, 0, eax, ebx, ecx, edx); if (ebx & bit_RDSEED) { #ifdef USE_RDRAND // CPUID.01H:ECX.RDRAND[bit 30] __cpuid(1, eax, ebx, ecx, edx); if (ecx & bit_RDRND) { _M_func = &__x86_rdseed_rdrand; return; } #endif _M_func = &__x86_rdseed; return; } } } #endif // USE_RDSEED #ifdef USE_RDRAND if (which & rdrand) { unsigned int eax, ebx, ecx, edx; // Check availability of cpuid and, for now at least, also the // CPU signature for Intel and AMD. if (__get_cpuid_max(0, &ebx) > 0 && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx)) { // CPUID.01H:ECX.RDRAND[bit 30] __cpuid(1, eax, ebx, ecx, edx); if (ecx & bit_RDRND) { _M_func = &__x86_rdrand; return; } } } #endif // USE_RDRAND #ifdef USE_DARN if (which & darn) { if (__builtin_cpu_supports("darn")) { _M_func = &__ppc_darn; return; } } #endif // USE_DARN #ifdef _GLIBCXX_HAVE_ARC4RANDOM if (which & arc4random) { _M_func = &__libc_arc4random; return; } #endif // _GLIBCXX_HAVE_ARC4RANDOM #ifdef _GLIBCXX_HAVE_GETENTROPY if (which & getentropy) { unsigned int i; if (::getentropy(&i, sizeof(i)) == 0) // On linux the syscall can fail. { _M_func = &__libc_getentropy; return; } } #endif // _GLIBCXX_HAVE_GETENTROPY #ifdef _GLIBCXX_USE_DEV_RANDOM if (which & device_file) { #ifdef USE_POSIX_FILE_IO _M_fd = ::open(fname, O_RDONLY); if (_M_fd != -1) { // Set _M_file to non-null so that _M_fini() will do clean up. _M_file = &_M_fd; return; } #else // USE_POSIX_FILE_IO _M_file = static_cast(std::fopen(fname, "rb")); if (_M_file) return; #endif // USE_POSIX_FILE_IO } #endif // _GLIBCXX_USE_DEV_RANDOM #ifdef USE_LCG // Either "prng" was requested explicitly, or "default" was requested // but nothing above worked, use a PRNG. if (which & prng) { static_assert(sizeof(lcg_type) <= sizeof(_M_fd), ""); static_assert(alignof(lcg_type) <= alignof(_M_fd), ""); _M_file = construct_lcg_at(&_M_fd); _M_func = &__lcg; return; } #endif std::__throw_runtime_error( __N("random_device::random_device(const std::string&):" " device not available")); #endif // USE_MT19937 } // This function is called by _M_init for targets that use mt19937 for // randomness, and by code compiled against old releases of libstdc++. void random_device::_M_init_pretr1(const std::string& token) { #ifdef USE_MT19937 unsigned long seed = 5489UL; if (token != "default" && token != "mt19937" && token != "prng") { const char* nptr = token.c_str(); char* endptr; seed = std::strtoul(nptr, &endptr, 0); if (*nptr == '\0' || *endptr != '\0') std::__throw_syserr(EINVAL, __N("random_device::_M_init_pretr1" "(const std::string&)")); } _M_mt.seed(seed); #else // Convert old default token "mt19937" or numeric seed tokens to "default". if (token == "mt19937" || std::isdigit((unsigned char)token[0])) _M_init("default"); else _M_init(token); #endif } // Called by old ABI version of random_device::_M_init(const std::string&). void random_device::_M_init(const char* s, size_t len) { const std::string token(s, len); #ifdef USE_MT19937 _M_init_pretr1(token); #else _M_init(token); #endif } void random_device::_M_fini() { // _M_file == nullptr means no resources to free. if (!_M_file) return; #if USE_LCG if (_M_func == &__lcg) { destroy_lcg_at(_M_file); return; } #endif #ifdef _GLIBCXX_USE_DEV_RANDOM #ifdef USE_POSIX_FILE_IO ::close(_M_fd); _M_fd = -1; #else std::fclose(static_cast(_M_file)); #endif _M_file = nullptr; #endif } random_device::result_type random_device::_M_getval() { #ifdef USE_MT19937 return _M_mt(); #else if (_M_func) return _M_func(_M_file); result_type ret; void* p = &ret; size_t n = sizeof(result_type); #ifdef USE_POSIX_FILE_IO do { const int e = ::read(_M_fd, p, n); if (e > 0) { n -= e; p = static_cast(p) + e; } else if (e != -1 || errno != EINTR) __throw_syserr(errno, __N("random_device could not be read")); } while (n > 0); #else // USE_POSIX_FILE_IO const size_t e = std::fread(p, n, 1, static_cast(_M_file)); if (e != 1) __throw_runtime_error(__N("random_device could not be read")); #endif // USE_POSIX_FILE_IO return ret; #endif // USE_MT19937 } // Only called by code compiled against old releases of libstdc++. // Forward the call to _M_getval() and let it decide what to do. random_device::result_type random_device::_M_getval_pretr1() { return _M_getval(); } double random_device::_M_getentropy() const noexcept { const int max = sizeof(result_type) * __CHAR_BIT__; switch(which_source(_M_func, _M_file)) { case rdrand: case rdseed: case darn: return (double) max; case arc4random: case getentropy: return (double) max; case rand_s: case prng: return 0.0; case device_file: // handled below break; default: return 0.0; } #if defined _GLIBCXX_USE_DEV_RANDOM \ && defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT #ifdef USE_POSIX_FILE_IO const int fd = _M_fd; #else const int fd = ::fileno(static_cast(_M_file)); #endif if (fd < 0) return 0.0; int ent; if (::ioctl(fd, RNDGETENTCNT, &ent) < 0) return 0.0; if (ent < 0) return 0.0; if (ent > max) ent = max; return static_cast(ent); #else return 0.0; #endif // _GLIBCXX_USE_DEV_RANDOM && _GLIBCXX_HAVE_SYS_IOCTL_H && RNDGETENTCNT } #ifdef USE_MT19937 template class mersenne_twister_engine< uint_fast32_t, 32, 624, 397, 31, 0x9908b0dfUL, 11, 0xffffffffUL, 7, 0x9d2c5680UL, 15, 0xefc60000UL, 18, 1812433253UL>; #endif // USE_MT19937 #ifdef USE_LCG template class linear_congruential_engine; template struct __detail::_Mod; #endif }