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