xref: /netbsd-src/external/gpl3/gcc.old/dist/libstdc++-v3/src/c++11/random.cc (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
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