1 // random -*- C++ -*- 2 3 // Copyright (C) 2012-2020 Free Software Foundation, Inc. 4 // 5 // This file is part of the GNU ISO C++ Library. This library is free 6 // software; you can redistribute it and/or modify it under the 7 // terms of the GNU General Public License as published by the 8 // Free Software Foundation; either version 3, or (at your option) 9 // any later version. 10 11 // This library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 16 // Under Section 7 of GPL version 3, you are granted additional 17 // permissions described in the GCC Runtime Library Exception, version 18 // 3.1, as published by the Free Software Foundation. 19 20 // You should have received a copy of the GNU General Public License and 21 // a copy of the GCC Runtime Library Exception along with this program; 22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 // <http://www.gnu.org/licenses/>. 24 25 #define _GLIBCXX_USE_CXX11_ABI 1 26 #define _CRT_RAND_S // define this before including <stdlib.h> to get rand_s 27 28 #include <random> 29 30 #ifdef _GLIBCXX_USE_C99_STDINT_TR1 31 32 #if defined __i386__ || defined __x86_64__ 33 # include <cpuid.h> 34 # ifdef _GLIBCXX_X86_RDRAND 35 # define USE_RDRAND 1 36 # endif 37 # ifdef _GLIBCXX_X86_RDSEED 38 # define USE_RDSEED 1 39 # endif 40 #endif 41 42 #include <cerrno> 43 #include <cstdio> 44 #include <cctype> // For std::isdigit. 45 46 #if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H 47 # include <unistd.h> 48 # include <fcntl.h> 49 // Use POSIX open, close, read etc. instead of ISO fopen, fclose, fread 50 # define USE_POSIX_FILE_IO 51 #endif 52 53 #ifdef _GLIBCXX_HAVE_SYS_IOCTL_H 54 # include <sys/ioctl.h> 55 #endif 56 57 #ifdef _GLIBCXX_HAVE_LINUX_TYPES_H 58 # include <linux/types.h> 59 #endif 60 61 #ifdef _GLIBCXX_HAVE_LINUX_RANDOM_H 62 # include <linux/random.h> 63 #endif 64 65 #ifdef _GLIBCXX_USE_CRT_RAND_S 66 # include <stdlib.h> 67 #endif 68 69 #if defined USE_RDRAND || defined USE_RDSEED \ 70 || defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM 71 # pragma GCC poison _M_mt 72 #else 73 // Use the mt19937 member of the union, as in previous GCC releases. 74 # define USE_MT19937 1 75 #endif 76 77 namespace std _GLIBCXX_VISIBILITY(default) 78 { 79 namespace 80 { 81 #if USE_RDRAND 82 unsigned int 83 __attribute__ ((target("rdrnd"))) __x86_rdrand(void *)84 __x86_rdrand(void*) 85 { 86 unsigned int retries = 100; 87 unsigned int val; 88 89 while (__builtin_ia32_rdrand32_step(&val) == 0) 90 if (--retries == 0) 91 std::__throw_runtime_error(__N("random_device: rdrand failed")); 92 93 return val; 94 } 95 #endif 96 97 #if USE_RDSEED 98 unsigned int 99 __attribute__ ((target("rdseed"))) __x86_rdseed(void * fallback)100 __x86_rdseed(void* fallback) 101 { 102 unsigned int retries = 100; 103 unsigned int val; 104 105 while (__builtin_ia32_rdseed_si_step(&val) == 0) 106 { 107 if (--retries == 0) 108 { 109 if (auto f = reinterpret_cast<unsigned int(*)(void*)>(fallback)) 110 return f(nullptr); 111 std::__throw_runtime_error(__N("random_device: rdseed failed")); 112 } 113 __builtin_ia32_pause(); 114 } 115 116 return val; 117 } 118 119 #if USE_RDRAND 120 unsigned int 121 __attribute__ ((target("rdseed,rdrnd"))) __x86_rdseed_rdrand(void *)122 __x86_rdseed_rdrand(void*) 123 { 124 return __x86_rdseed(reinterpret_cast<void*>(&__x86_rdrand)); 125 } 126 #endif 127 #endif 128 129 #ifdef _GLIBCXX_USE_CRT_RAND_S 130 unsigned int __winxp_rand_s(void *)131 __winxp_rand_s(void*) 132 { 133 unsigned int val; 134 if (::rand_s(&val) != 0) 135 std::__throw_runtime_error(__N("random_device: rand_s failed")); 136 return val; 137 } 138 #endif 139 } 140 141 void _M_init(const std::string & token)142 random_device::_M_init(const std::string& token) 143 { 144 #ifdef USE_MT19937 145 // If no real random device is supported then use the mt19937 engine. 146 _M_init_pretr1(token); 147 return; 148 #else 149 150 _M_file = nullptr; 151 _M_func = nullptr; 152 _M_fd = -1; 153 154 const char* fname [[gnu::unused]] = nullptr; 155 bool default_token [[gnu::unused]] = false; 156 157 enum { rand_s, rdseed, rdrand, device_file } which; 158 159 if (token == "default") 160 { 161 default_token = true; 162 fname = "/dev/urandom"; 163 #if defined _GLIBCXX_USE_CRT_RAND_S 164 which = rand_s; 165 #elif defined USE_RDSEED 166 which = rdseed; 167 #elif defined USE_RDRAND 168 which = rdrand; 169 #elif defined _GLIBCXX_USE_DEV_RANDOM 170 which = device_file; 171 #else 172 # error "either define USE_MT19937 above or set the default device here" 173 #endif 174 } 175 #ifdef USE_RDSEED 176 else if (token == "rdseed") 177 which = rdseed; 178 #endif // USE_RDSEED 179 #ifdef USE_RDRAND 180 else if (token == "rdrand" || token == "rdrnd") 181 which = rdrand; 182 #endif // USE_RDRAND 183 #ifdef _GLIBCXX_USE_CRT_RAND_S 184 else if (token == "rand_s") 185 which = rand_s; 186 #endif // _GLIBCXX_USE_CRT_RAND_S 187 #ifdef _GLIBCXX_USE_DEV_RANDOM 188 else if (token == "/dev/urandom" || token == "/dev/random") 189 { 190 fname = token.c_str(); 191 which = device_file; 192 } 193 #endif // _GLIBCXX_USE_DEV_RANDOM 194 else 195 std::__throw_runtime_error( 196 __N("random_device::random_device(const std::string&):" 197 " unsupported token")); 198 199 switch (which) 200 { 201 #ifdef _GLIBCXX_USE_CRT_RAND_S 202 case rand_s: 203 { 204 _M_func = &__winxp_rand_s; 205 return; 206 } 207 #endif // _GLIBCXX_USE_CRT_RAND_S 208 #ifdef USE_RDSEED 209 case rdseed: 210 { 211 unsigned int eax, ebx, ecx, edx; 212 // Check availability of cpuid and, for now at least, also the 213 // CPU signature for Intel and AMD. 214 if (__get_cpuid_max(0, &ebx) > 0 215 && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx)) 216 { 217 // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18] 218 __cpuid_count(7, 0, eax, ebx, ecx, edx); 219 if (ebx & bit_RDSEED) 220 { 221 #ifdef USE_RDRAND 222 // CPUID.01H:ECX.RDRAND[bit 30] 223 __cpuid(1, eax, ebx, ecx, edx); 224 if (ecx & bit_RDRND) 225 { 226 _M_func = &__x86_rdseed_rdrand; 227 return; 228 } 229 #endif 230 _M_func = &__x86_rdseed; 231 return; 232 } 233 } 234 // If rdseed was explicitly requested then we're done here. 235 if (!default_token) 236 break; 237 // Otherwise fall through to try the next available option. 238 [[gnu::fallthrough]]; 239 } 240 #endif // USE_RDSEED 241 #ifdef USE_RDRAND 242 case rdrand: 243 { 244 unsigned int eax, ebx, ecx, edx; 245 // Check availability of cpuid and, for now at least, also the 246 // CPU signature for Intel and AMD. 247 if (__get_cpuid_max(0, &ebx) > 0 248 && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx)) 249 { 250 // CPUID.01H:ECX.RDRAND[bit 30] 251 __cpuid(1, eax, ebx, ecx, edx); 252 if (ecx & bit_RDRND) 253 { 254 _M_func = &__x86_rdrand; 255 return; 256 } 257 } 258 // If rdrand was explicitly requested then we're done here. 259 if (!default_token) 260 break; 261 // Otherwise fall through to try the next available option. 262 [[gnu::fallthrough]]; 263 } 264 #endif // USE_RDRAND 265 #ifdef _GLIBCXX_USE_DEV_RANDOM 266 case device_file: 267 { 268 #ifdef USE_POSIX_FILE_IO 269 _M_fd = ::open(fname, O_RDONLY); 270 if (_M_fd != -1) 271 { 272 // Set _M_file to non-null so that _M_fini() will do clean up. 273 _M_file = &_M_fd; 274 return; 275 } 276 #else // USE_POSIX_FILE_IO 277 _M_file = static_cast<void*>(std::fopen(fname, "rb")); 278 if (_M_file) 279 return; 280 #endif // USE_POSIX_FILE_IO 281 [[gnu::fallthrough]]; 282 } 283 #endif // _GLIBCXX_USE_DEV_RANDOM 284 default: 285 { } 286 } 287 std::__throw_runtime_error( 288 __N("random_device::random_device(const std::string&):" 289 " device not available")); 290 #endif // USE_MT19937 291 } 292 293 // This function is called by _M_init for targets that use mt19937 for 294 // randomness, and by code compiled against old releases of libstdc++. 295 void _M_init_pretr1(const std::string & token)296 random_device::_M_init_pretr1(const std::string& token) 297 { 298 #ifdef USE_MT19937 299 unsigned long seed = 5489UL; 300 if (token != "default" && token != "mt19937") 301 { 302 const char* nptr = token.c_str(); 303 char* endptr; 304 seed = std::strtoul(nptr, &endptr, 0); 305 if (*nptr == '\0' || *endptr != '\0') 306 std::__throw_runtime_error(__N("random_device::_M_init_pretr1" 307 "(const std::string&)")); 308 } 309 _M_mt.seed(seed); 310 #else 311 // Convert old default token "mt19937" or numeric seed tokens to "default". 312 if (token == "mt19937" || std::isdigit((unsigned char)token[0])) 313 _M_init("default"); 314 else 315 _M_init(token); 316 #endif 317 } 318 319 // Called by old ABI version of random_device::_M_init(const std::string&). 320 void _M_init(const char * s,size_t len)321 random_device::_M_init(const char* s, size_t len) 322 { 323 const std::string token(s, len); 324 #ifdef USE_MT19937 325 _M_init_pretr1(token); 326 #else 327 _M_init(token); 328 #endif 329 } 330 331 void _M_fini()332 random_device::_M_fini() 333 { 334 // _M_file == nullptr means no resources to free. 335 if (!_M_file) 336 return; 337 338 #ifdef USE_POSIX_FILE_IO 339 ::close(_M_fd); 340 _M_fd = -1; 341 #else 342 std::fclose(static_cast<FILE*>(_M_file)); 343 #endif 344 _M_file = nullptr; 345 } 346 347 random_device::result_type _M_getval()348 random_device::_M_getval() 349 { 350 #ifdef USE_MT19937 351 return _M_mt(); 352 #else 353 354 #if defined USE_RDRAND || defined USE_RDSEED || defined _GLIBCXX_USE_CRT_RAND_S 355 if (_M_func) 356 return _M_func(nullptr); 357 #endif 358 359 result_type ret; 360 void* p = &ret; 361 size_t n = sizeof(result_type); 362 #ifdef USE_POSIX_FILE_IO 363 do 364 { 365 const int e = ::read(_M_fd, p, n); 366 if (e > 0) 367 { 368 n -= e; 369 p = static_cast<char*>(p) + e; 370 } 371 else if (e != -1 || errno != EINTR) 372 __throw_runtime_error(__N("random_device could not be read")); 373 } 374 while (n > 0); 375 #else // USE_POSIX_FILE_IO 376 const size_t e = std::fread(p, n, 1, static_cast<FILE*>(_M_file)); 377 if (e != 1) 378 __throw_runtime_error(__N("random_device could not be read")); 379 #endif // USE_POSIX_FILE_IO 380 381 return ret; 382 #endif // USE_MT19937 383 } 384 385 // Only called by code compiled against old releases of libstdc++. 386 // Forward the call to _M_getval() and let it decide what to do. 387 random_device::result_type _M_getval_pretr1()388 random_device::_M_getval_pretr1() 389 { return _M_getval(); } 390 391 double _M_getentropy() const392 random_device::_M_getentropy() const noexcept 393 { 394 #if defined _GLIBCXX_USE_DEV_RANDOM \ 395 && defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT 396 if (!_M_file) 397 return 0.0; 398 399 #ifdef USE_POSIX_FILE_IO 400 const int fd = _M_fd; 401 #else 402 const int fd = ::fileno(static_cast<FILE*>(_M_file)); 403 #endif 404 if (fd < 0) 405 return 0.0; 406 407 int ent; 408 if (::ioctl(fd, RNDGETENTCNT, &ent) < 0) 409 return 0.0; 410 411 if (ent < 0) 412 return 0.0; 413 414 const int max = sizeof(result_type) * __CHAR_BIT__; 415 if (ent > max) 416 ent = max; 417 418 return static_cast<double>(ent); 419 #else 420 return 0.0; 421 #endif // _GLIBCXX_USE_DEV_RANDOM && _GLIBCXX_HAVE_SYS_IOCTL_H && RNDGETENTCNT 422 } 423 424 #ifdef USE_MT19937 425 template class mersenne_twister_engine< 426 uint_fast32_t, 427 32, 624, 397, 31, 428 0x9908b0dfUL, 11, 429 0xffffffffUL, 7, 430 0x9d2c5680UL, 15, 431 0xefc60000UL, 18, 1812433253UL>; 432 #endif // USE_MT19937 433 } 434 #endif // _GLIBCXX_USE_C99_STDINT_TR1 435