| // random -*- C++ -*- |
| |
| // Copyright (C) 2012-2020 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 |
| // <http://www.gnu.org/licenses/>. |
| |
| #define _GLIBCXX_USE_CXX11_ABI 1 |
| #define _CRT_RAND_S // define this before including <stdlib.h> to get rand_s |
| |
| #include <random> |
| |
| #ifdef _GLIBCXX_USE_C99_STDINT_TR1 |
| |
| #if defined __i386__ || defined __x86_64__ |
| # include <cpuid.h> |
| # ifdef _GLIBCXX_X86_RDRAND |
| # define USE_RDRAND 1 |
| # endif |
| # ifdef _GLIBCXX_X86_RDSEED |
| # define USE_RDSEED 1 |
| # endif |
| #endif |
| |
| #include <cerrno> |
| #include <cstdio> |
| #include <cctype> // For std::isdigit. |
| |
| #if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H |
| # include <unistd.h> |
| # include <fcntl.h> |
| // 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 <sys/ioctl.h> |
| #endif |
| |
| #ifdef _GLIBCXX_HAVE_LINUX_TYPES_H |
| # include <linux/types.h> |
| #endif |
| |
| #ifdef _GLIBCXX_HAVE_LINUX_RANDOM_H |
| # include <linux/random.h> |
| #endif |
| |
| #ifdef _GLIBCXX_USE_CRT_RAND_S |
| # include <stdlib.h> |
| #endif |
| |
| #if defined USE_RDRAND || defined USE_RDSEED \ |
| || defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM |
| # pragma GCC poison _M_mt |
| #else |
| // Use the mt19937 member of the union, as in previous GCC releases. |
| # define USE_MT19937 1 |
| #endif |
| |
| namespace std _GLIBCXX_VISIBILITY(default) |
| { |
| namespace |
| { |
| #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) |
| 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) |
| { |
| if (--retries == 0) |
| { |
| if (auto f = reinterpret_cast<unsigned int(*)(void*)>(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<void*>(&__x86_rdrand)); |
| } |
| #endif |
| #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 |
| } |
| |
| 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; |
| bool default_token [[gnu::unused]] = false; |
| |
| enum { rand_s, rdseed, rdrand, device_file } which; |
| |
| if (token == "default") |
| { |
| default_token = true; |
| fname = "/dev/urandom"; |
| #if defined _GLIBCXX_USE_CRT_RAND_S |
| which = rand_s; |
| #elif defined USE_RDSEED |
| which = rdseed; |
| #elif defined USE_RDRAND |
| which = rdrand; |
| #elif defined _GLIBCXX_USE_DEV_RANDOM |
| which = device_file; |
| #else |
| # error "either define USE_MT19937 above or set the default device here" |
| #endif |
| } |
| #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 _GLIBCXX_USE_CRT_RAND_S |
| else if (token == "rand_s") |
| which = rand_s; |
| #endif // _GLIBCXX_USE_CRT_RAND_S |
| #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 |
| else |
| std::__throw_runtime_error( |
| __N("random_device::random_device(const std::string&):" |
| " unsupported token")); |
| |
| switch (which) |
| { |
| #ifdef _GLIBCXX_USE_CRT_RAND_S |
| case rand_s: |
| { |
| _M_func = &__winxp_rand_s; |
| return; |
| } |
| #endif // _GLIBCXX_USE_CRT_RAND_S |
| #ifdef USE_RDSEED |
| case 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; |
| } |
| } |
| // If rdseed was explicitly requested then we're done here. |
| if (!default_token) |
| break; |
| // Otherwise fall through to try the next available option. |
| [[gnu::fallthrough]]; |
| } |
| #endif // USE_RDSEED |
| #ifdef USE_RDRAND |
| case 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; |
| } |
| } |
| // If rdrand was explicitly requested then we're done here. |
| if (!default_token) |
| break; |
| // Otherwise fall through to try the next available option. |
| [[gnu::fallthrough]]; |
| } |
| #endif // USE_RDRAND |
| #ifdef _GLIBCXX_USE_DEV_RANDOM |
| case 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<void*>(std::fopen(fname, "rb")); |
| if (_M_file) |
| return; |
| #endif // USE_POSIX_FILE_IO |
| [[gnu::fallthrough]]; |
| } |
| #endif // _GLIBCXX_USE_DEV_RANDOM |
| default: |
| { } |
| } |
| 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") |
| { |
| const char* nptr = token.c_str(); |
| char* endptr; |
| seed = std::strtoul(nptr, &endptr, 0); |
| if (*nptr == '\0' || *endptr != '\0') |
| std::__throw_runtime_error(__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; |
| |
| #ifdef USE_POSIX_FILE_IO |
| ::close(_M_fd); |
| _M_fd = -1; |
| #else |
| std::fclose(static_cast<FILE*>(_M_file)); |
| #endif |
| _M_file = nullptr; |
| } |
| |
| random_device::result_type |
| random_device::_M_getval() |
| { |
| #ifdef USE_MT19937 |
| return _M_mt(); |
| #else |
| |
| #if defined USE_RDRAND || defined USE_RDSEED || defined _GLIBCXX_USE_CRT_RAND_S |
| if (_M_func) |
| return _M_func(nullptr); |
| #endif |
| |
| 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<char*>(p) + e; |
| } |
| else if (e != -1 || errno != EINTR) |
| __throw_runtime_error(__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<FILE*>(_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 |
| { |
| #if defined _GLIBCXX_USE_DEV_RANDOM \ |
| && defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT |
| if (!_M_file) |
| return 0.0; |
| |
| #ifdef USE_POSIX_FILE_IO |
| const int fd = _M_fd; |
| #else |
| const int fd = ::fileno(static_cast<FILE*>(_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; |
| |
| const int max = sizeof(result_type) * __CHAR_BIT__; |
| if (ent > max) |
| ent = max; |
| |
| return static_cast<double>(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 |
| } |
| #endif // _GLIBCXX_USE_C99_STDINT_TR1 |