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__ ((noinline)) 84 # ifndef __clang__ 85 __attribute__ ((target("rdrnd"))) 86 # endif __x86_rdrand(void *)87 __x86_rdrand(void*) 88 { 89 unsigned int retries = 100; 90 unsigned int val; 91 92 while (__builtin_ia32_rdrand32_step(&val) == 0) 93 if (--retries == 0) 94 std::__throw_runtime_error(__N("random_device: rdrand failed")); 95 96 return val; 97 } 98 #endif 99 100 #if USE_RDSEED 101 unsigned int 102 __attribute__ ((noinline)) 103 # ifndef __clang__ 104 __attribute__ ((target("rdseed"))) 105 # endif __x86_rdseed(void * fallback)106 __x86_rdseed(void* fallback) 107 { 108 unsigned int retries = 100; 109 unsigned int val; 110 111 while (__builtin_ia32_rdseed_si_step(&val) == 0) 112 { 113 if (--retries == 0) 114 { 115 if (auto f = reinterpret_cast<unsigned int(*)(void*)>(fallback)) 116 return f(nullptr); 117 std::__throw_runtime_error(__N("random_device: rdseed failed")); 118 } 119 __builtin_ia32_pause(); 120 } 121 122 return val; 123 } 124 125 #if USE_RDRAND 126 unsigned int 127 __attribute__ ((target("rdseed,rdrnd"))) __x86_rdseed_rdrand(void *)128 __x86_rdseed_rdrand(void*) 129 { 130 return __x86_rdseed(reinterpret_cast<void*>(&__x86_rdrand)); 131 } 132 #endif 133 #endif 134 135 #ifdef _GLIBCXX_USE_CRT_RAND_S 136 unsigned int __winxp_rand_s(void *)137 __winxp_rand_s(void*) 138 { 139 unsigned int val; 140 if (::rand_s(&val) != 0) 141 std::__throw_runtime_error(__N("random_device: rand_s failed")); 142 return val; 143 } 144 #endif 145 } 146 147 void _M_init(const std::string & token)148 random_device::_M_init(const std::string& token) 149 { 150 #ifdef USE_MT19937 151 // If no real random device is supported then use the mt19937 engine. 152 _M_init_pretr1(token); 153 return; 154 #else 155 156 _M_file = nullptr; 157 _M_func = nullptr; 158 _M_fd = -1; 159 160 const char* fname [[gnu::unused]] = nullptr; 161 bool default_token [[gnu::unused]] = false; 162 163 enum { rand_s, rdseed, rdrand, device_file } which; 164 165 if (token == "default") 166 { 167 default_token = true; 168 fname = "/dev/urandom"; 169 #if defined _GLIBCXX_USE_CRT_RAND_S 170 which = rand_s; 171 #elif defined USE_RDSEED 172 which = rdseed; 173 #elif defined USE_RDRAND 174 which = rdrand; 175 #elif defined _GLIBCXX_USE_DEV_RANDOM 176 which = device_file; 177 #else 178 # error "either define USE_MT19937 above or set the default device here" 179 #endif 180 } 181 #ifdef USE_RDSEED 182 else if (token == "rdseed") 183 which = rdseed; 184 #endif // USE_RDSEED 185 #ifdef USE_RDRAND 186 else if (token == "rdrand" || token == "rdrnd") 187 which = rdrand; 188 #endif // USE_RDRAND 189 #ifdef _GLIBCXX_USE_CRT_RAND_S 190 else if (token == "rand_s") 191 which = rand_s; 192 #endif // _GLIBCXX_USE_CRT_RAND_S 193 #ifdef _GLIBCXX_USE_DEV_RANDOM 194 else if (token == "/dev/urandom" || token == "/dev/random") 195 { 196 fname = token.c_str(); 197 which = device_file; 198 } 199 #endif // _GLIBCXX_USE_DEV_RANDOM 200 else 201 std::__throw_runtime_error( 202 __N("random_device::random_device(const std::string&):" 203 " unsupported token")); 204 205 switch (which) 206 { 207 #ifdef _GLIBCXX_USE_CRT_RAND_S 208 case rand_s: 209 { 210 _M_func = &__winxp_rand_s; 211 return; 212 } 213 #endif // _GLIBCXX_USE_CRT_RAND_S 214 #ifdef USE_RDSEED 215 case rdseed: 216 { 217 unsigned int eax, ebx, ecx, edx; 218 // Check availability of cpuid and, for now at least, also the 219 // CPU signature for Intel and AMD. 220 if (__get_cpuid_max(0, &ebx) > 0 221 && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx)) 222 { 223 // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18] 224 __cpuid_count(7, 0, eax, ebx, ecx, edx); 225 if (ebx & bit_RDSEED) 226 { 227 #ifdef USE_RDRAND 228 // CPUID.01H:ECX.RDRAND[bit 30] 229 __cpuid(1, eax, ebx, ecx, edx); 230 if (ecx & bit_RDRND) 231 { 232 _M_func = &__x86_rdseed_rdrand; 233 return; 234 } 235 #endif 236 _M_func = &__x86_rdseed; 237 return; 238 } 239 } 240 // If rdseed was explicitly requested then we're done here. 241 if (!default_token) 242 break; 243 // Otherwise fall through to try the next available option. 244 [[gnu::fallthrough]]; 245 } 246 #endif // USE_RDSEED 247 #ifdef USE_RDRAND 248 case rdrand: 249 { 250 unsigned int eax, ebx, ecx, edx; 251 // Check availability of cpuid and, for now at least, also the 252 // CPU signature for Intel and AMD. 253 if (__get_cpuid_max(0, &ebx) > 0 254 && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx)) 255 { 256 // CPUID.01H:ECX.RDRAND[bit 30] 257 __cpuid(1, eax, ebx, ecx, edx); 258 if (ecx & bit_RDRND) 259 { 260 _M_func = &__x86_rdrand; 261 return; 262 } 263 } 264 // If rdrand was explicitly requested then we're done here. 265 if (!default_token) 266 break; 267 // Otherwise fall through to try the next available option. 268 [[gnu::fallthrough]]; 269 } 270 #endif // USE_RDRAND 271 #ifdef _GLIBCXX_USE_DEV_RANDOM 272 case device_file: 273 { 274 #ifdef USE_POSIX_FILE_IO 275 _M_fd = ::open(fname, O_RDONLY); 276 if (_M_fd != -1) 277 { 278 // Set _M_file to non-null so that _M_fini() will do clean up. 279 _M_file = &_M_fd; 280 return; 281 } 282 #else // USE_POSIX_FILE_IO 283 _M_file = static_cast<void*>(std::fopen(fname, "rb")); 284 if (_M_file) 285 return; 286 #endif // USE_POSIX_FILE_IO 287 [[gnu::fallthrough]]; 288 } 289 #endif // _GLIBCXX_USE_DEV_RANDOM 290 default: 291 { } 292 } 293 std::__throw_runtime_error( 294 __N("random_device::random_device(const std::string&):" 295 " device not available")); 296 #endif // USE_MT19937 297 } 298 299 // This function is called by _M_init for targets that use mt19937 for 300 // randomness, and by code compiled against old releases of libstdc++. 301 void _M_init_pretr1(const std::string & token)302 random_device::_M_init_pretr1(const std::string& token) 303 { 304 #ifdef USE_MT19937 305 unsigned long seed = 5489UL; 306 if (token != "default" && token != "mt19937") 307 { 308 const char* nptr = token.c_str(); 309 char* endptr; 310 seed = std::strtoul(nptr, &endptr, 0); 311 if (*nptr == '\0' || *endptr != '\0') 312 std::__throw_runtime_error(__N("random_device::_M_init_pretr1" 313 "(const std::string&)")); 314 } 315 _M_mt.seed(seed); 316 #else 317 // Convert old default token "mt19937" or numeric seed tokens to "default". 318 if (token == "mt19937" || std::isdigit((unsigned char)token[0])) 319 _M_init("default"); 320 else 321 _M_init(token); 322 #endif 323 } 324 325 // Called by old ABI version of random_device::_M_init(const std::string&). 326 void _M_init(const char * s,size_t len)327 random_device::_M_init(const char* s, size_t len) 328 { 329 const std::string token(s, len); 330 #ifdef USE_MT19937 331 _M_init_pretr1(token); 332 #else 333 _M_init(token); 334 #endif 335 } 336 337 void _M_fini()338 random_device::_M_fini() 339 { 340 // _M_file == nullptr means no resources to free. 341 if (!_M_file) 342 return; 343 344 #ifdef USE_POSIX_FILE_IO 345 ::close(_M_fd); 346 _M_fd = -1; 347 #else 348 std::fclose(static_cast<FILE*>(_M_file)); 349 #endif 350 _M_file = nullptr; 351 } 352 353 random_device::result_type _M_getval()354 random_device::_M_getval() 355 { 356 #ifdef USE_MT19937 357 return _M_mt(); 358 #else 359 360 #if defined USE_RDRAND || defined USE_RDSEED || defined _GLIBCXX_USE_CRT_RAND_S 361 if (_M_func) 362 return _M_func(nullptr); 363 #endif 364 365 result_type ret; 366 void* p = &ret; 367 size_t n = sizeof(result_type); 368 #ifdef USE_POSIX_FILE_IO 369 do 370 { 371 const int e = ::read(_M_fd, p, n); 372 if (e > 0) 373 { 374 n -= e; 375 p = static_cast<char*>(p) + e; 376 } 377 else if (e != -1 || errno != EINTR) 378 __throw_runtime_error(__N("random_device could not be read")); 379 } 380 while (n > 0); 381 #else // USE_POSIX_FILE_IO 382 const size_t e = std::fread(p, n, 1, static_cast<FILE*>(_M_file)); 383 if (e != 1) 384 __throw_runtime_error(__N("random_device could not be read")); 385 #endif // USE_POSIX_FILE_IO 386 387 return ret; 388 #endif // USE_MT19937 389 } 390 391 // Only called by code compiled against old releases of libstdc++. 392 // Forward the call to _M_getval() and let it decide what to do. 393 random_device::result_type _M_getval_pretr1()394 random_device::_M_getval_pretr1() 395 { return _M_getval(); } 396 397 double _M_getentropy() const398 random_device::_M_getentropy() const noexcept 399 { 400 #if defined _GLIBCXX_USE_DEV_RANDOM \ 401 && defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT 402 if (!_M_file) 403 return 0.0; 404 405 #ifdef USE_POSIX_FILE_IO 406 const int fd = _M_fd; 407 #else 408 const int fd = ::fileno(static_cast<FILE*>(_M_file)); 409 #endif 410 if (fd < 0) 411 return 0.0; 412 413 int ent; 414 if (::ioctl(fd, RNDGETENTCNT, &ent) < 0) 415 return 0.0; 416 417 if (ent < 0) 418 return 0.0; 419 420 const int max = sizeof(result_type) * __CHAR_BIT__; 421 if (ent > max) 422 ent = max; 423 424 return static_cast<double>(ent); 425 #else 426 return 0.0; 427 #endif // _GLIBCXX_USE_DEV_RANDOM && _GLIBCXX_HAVE_SYS_IOCTL_H && RNDGETENTCNT 428 } 429 430 #ifdef USE_MT19937 431 template class mersenne_twister_engine< 432 uint_fast32_t, 433 32, 624, 397, 31, 434 0x9908b0dfUL, 11, 435 0xffffffffUL, 7, 436 0x9d2c5680UL, 15, 437 0xefc60000UL, 18, 1812433253UL>; 438 #endif // USE_MT19937 439 } 440 #endif // _GLIBCXX_USE_C99_STDINT_TR1 441