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