From b0c0d878a8b5bf39dbea4c192fed26d340524439 Mon Sep 17 00:00:00 2001
From: Jonathan Wakely
Date: Wed, 29 May 2019 15:45:35 +0100
Subject: [PATCH] PR libstdc++/85494 use rdseed and rand_s in
std::random_device
Add support for additional sources of randomness to std::random_device,
to allow using RDSEED for Intel CPUs and rand_s for Windows. When
supported these can be selected using the tokens "rdseed" and "rand_s".
For *-w64-mingw32 targets the "default" token will now use rand_s, and
for other i?86-*-* and x86_64-*-* targets it will try to use "rdseed"
first, then "rdrand", and finally "/dev/urandom".
To simplify the declaration of std::random_device in the
constructors now unconditionally call _M_init instead of _M_init_pretr1,
and the function call operator now unconditionally calls _M_getval. The
library code now decides whether _M_init and _M_getval should use a real
source of randomness or the mt19937 engine.
Existing code compiled against old libstdc++ headers will still call
_M_init_pretr1 and _M_getval_pretr1, but those functions now forward to
_M_init and _M_getval if a real source of randomness is available. This
means existing code compiled for mingw-w64 will start to use rand_s just
by linking to a new libstdc++.dll.
* acinclude.m4 (GLIBCXX_CHECK_X86_RDSEED): Define macro to check if
the assembler supports rdseed.
* config.h.in: Regenerate.
* configure: Regenerate.
* configure.ac: Use GLIBCXX_CHECK_X86_RDSEED.
* config/os/mingw32-w64/os_defines.h (_GLIBCXX_USE_CRT_RAND_S): Define.
* doc/html/*: Regenerate.
* doc/xml/manual/status_cxx2011.xml: Document new tokens.
* include/bits/random.h (random_device::random_device()): Always call
_M_init rather than _M_init_pretr1.
(random_device::random_device(const string&)): Likewise.
(random_device::operator()()): Always call _M_getval().
(random_device::_M_file): Replace first member of union with an
anonymous struct, with _M_file as its first member.
* src/c++11/random.cc [_GLIBCXX_X86_RDRAND] (USE_RDRAND): Define.
[_GLIBCXX_X86_RDSEED] (USE_RDSEED): Define.
(USE_MT19937): Define if none of the above are defined.
(USE_POSIX_FILE_IO): Define.
(_M_strtoul): Remove.
[USE_RDSEED] (__x86_rdseed): Define new function.
[_GLIBCXX_USE_CRT_RAND_S] (__winxp_rand_s): Define new function.
(random_device::_M_init(const string&)): Initialize new union members.
Add support for "rdseed" and "rand_s" tokens. Decide what the
"default" token does according to which USE_* macros are defined.
[USE_POSIX_FILE_IO]: Store a file descriptor.
[USE_MT19937]: Forward to _M_init_pretr1 instead.
(random_device::_M_init_pretr1(const string&)) [USE_MT19937]: Inline
code from _M_strtoul.
[!USE_MT19937]: Call _M_init, transforming the old default token or
numeric tokens to "default".
(random_device::_M_fini()) [USE_POSIX_FILE_IO]: Use close not fclose.
(random_device::_M_getval()): Use new union members to obtain a
random number from the stored function pointer or file descriptor.
[USE_MT19937]: Obtain a value from the mt19937 engine.
(random_device::_M_getval_pretr1()): Call _M_getval().
(random_device::_M_getentropy()) [USE_POSIX_FILE_IO]: Use _M_fd
instead of fileno.
[!USE_MT19937] (mersenne_twister): Do not instantiate when not needed.
* testsuite/26_numerics/random/random_device/85494.cc: New test.
From-SVN: r271740
---
libstdc++-v3/ChangeLog | 43 +++
libstdc++-v3/acinclude.m4 | 20 ++
libstdc++-v3/config.h.in | 3 +
.../config/os/mingw32-w64/os_defines.h | 2 +
libstdc++-v3/configure | 41 +++
libstdc++-v3/configure.ac | 2 +
.../html/manual/appendix_contributing.html | 2 +-
libstdc++-v3/doc/html/manual/status.html | 42 ++-
.../doc/xml/manual/status_cxx2011.xml | 66 +++-
libstdc++-v3/include/bits/random.h | 23 +-
libstdc++-v3/src/c++11/random.cc | 301 ++++++++++++++----
.../26_numerics/random/random_device/85494.cc | 40 +++
12 files changed, 490 insertions(+), 95 deletions(-)
create mode 100644 libstdc++-v3/testsuite/26_numerics/random/random_device/85494.cc
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 31f7766b831..a01b54e8b63 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,46 @@
+2019-05-29 Jonathan Wakely
+
+ PR libstdc++/85494 use rdseed and rand_s in std::random_device
+ * acinclude.m4 (GLIBCXX_CHECK_X86_RDSEED): Define macro to check if
+ the assembler supports rdseed.
+ * config.h.in: Regenerate.
+ * configure: Regenerate.
+ * configure.ac: Use GLIBCXX_CHECK_X86_RDSEED.
+ * config/os/mingw32-w64/os_defines.h (_GLIBCXX_USE_CRT_RAND_S): Define.
+ * doc/html/*: Regenerate.
+ * doc/xml/manual/status_cxx2011.xml: Document new tokens.
+ * include/bits/random.h (random_device::random_device()): Always call
+ _M_init rather than _M_init_pretr1.
+ (random_device::random_device(const string&)): Likewise.
+ (random_device::operator()()): Always call _M_getval().
+ (random_device::_M_file): Replace first member of union with an
+ anonymous struct, with _M_file as its first member.
+ * src/c++11/random.cc [_GLIBCXX_X86_RDRAND] (USE_RDRAND): Define.
+ [_GLIBCXX_X86_RDSEED] (USE_RDSEED): Define.
+ (USE_MT19937): Define if none of the above are defined.
+ (USE_POSIX_FILE_IO): Define.
+ (_M_strtoul): Remove.
+ [USE_RDSEED] (__x86_rdseed): Define new function.
+ [_GLIBCXX_USE_CRT_RAND_S] (__winxp_rand_s): Define new function.
+ (random_device::_M_init(const string&)): Initialize new union members.
+ Add support for "rdseed" and "rand_s" tokens. Decide what the
+ "default" token does according to which USE_* macros are defined.
+ [USE_POSIX_FILE_IO]: Store a file descriptor.
+ [USE_MT19937]: Forward to _M_init_pretr1 instead.
+ (random_device::_M_init_pretr1(const string&)) [USE_MT19937]: Inline
+ code from _M_strtoul.
+ [!USE_MT19937]: Call _M_init, transforming the old default token or
+ numeric tokens to "default".
+ (random_device::_M_fini()) [USE_POSIX_FILE_IO]: Use close not fclose.
+ (random_device::_M_getval()): Use new union members to obtain a
+ random number from the stored function pointer or file descriptor.
+ [USE_MT19937]: Obtain a value from the mt19937 engine.
+ (random_device::_M_getval_pretr1()): Call _M_getval().
+ (random_device::_M_getentropy()) [USE_POSIX_FILE_IO]: Use _M_fd
+ instead of fileno.
+ [!USE_MT19937] (mersenne_twister): Do not instantiate when not needed.
+ * testsuite/26_numerics/random/random_device/85494.cc: New test.
+
2019-05-28 Jonathan Wakely
PR libstdc++/90634
diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index 84258d87a33..19e9f14b0f5 100644
--- a/libstdc++-v3/acinclude.m4
+++ b/libstdc++-v3/acinclude.m4
@@ -4053,6 +4053,26 @@ AC_DEFUN([GLIBCXX_CHECK_X86_RDRAND], [
AC_MSG_RESULT($ac_cv_x86_rdrand)
])
+dnl
+dnl Check whether rdseed is supported in the assembler.
+AC_DEFUN([GLIBCXX_CHECK_X86_RDSEED], [
+ AC_MSG_CHECKING([for rdseed support in assembler])
+ AC_CACHE_VAL(ac_cv_x86_rdseed, [
+ ac_cv_x86_rdseed=no
+ case "$target" in
+ i?86-*-* | \
+ x86_64-*-*)
+ AC_TRY_COMPILE(, [asm("rdseed %eax");],
+ [ac_cv_x86_rdseed=yes], [ac_cv_x86_rdseed=no])
+ esac
+ ])
+ if test $ac_cv_x86_rdseed = yes; then
+ AC_DEFINE(_GLIBCXX_X86_RDSEED, 1,
+ [ Defined if as can handle rdseed. ])
+ fi
+ AC_MSG_RESULT($ac_cv_x86_rdseed)
+])
+
dnl
dnl Check whether get_nprocs is available in , and define _GLIBCXX_USE_GET_NPROCS.
dnl
diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in
index 3a6f180c06d..99286e67a27 100644
--- a/libstdc++-v3/config.h.in
+++ b/libstdc++-v3/config.h.in
@@ -1038,6 +1038,9 @@
/* Defined if as can handle rdrand. */
#undef _GLIBCXX_X86_RDRAND
+/* Defined if as can handle rdseed. */
+#undef _GLIBCXX_X86_RDSEED
+
/* Define to 1 if mutex_timedlock is available. */
#undef _GTHREAD_USE_MUTEX_TIMEDLOCK
diff --git a/libstdc++-v3/config/os/mingw32-w64/os_defines.h b/libstdc++-v3/config/os/mingw32-w64/os_defines.h
index 6c19d3953a6..418c6f569df 100644
--- a/libstdc++-v3/config/os/mingw32-w64/os_defines.h
+++ b/libstdc++-v3/config/os/mingw32-w64/os_defines.h
@@ -88,4 +88,6 @@
// See libstdc++/59807
#define _GTHREAD_USE_MUTEX_INIT_FUNC 1
+#define _GLIBCXX_USE_CRT_RAND_S 1
+
#endif
diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure
index 7d134a0034b..7c45c340677 100755
--- a/libstdc++-v3/configure
+++ b/libstdc++-v3/configure
@@ -79870,6 +79870,47 @@ $as_echo "#define _GLIBCXX_X86_RDRAND 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_x86_rdrand" >&5
$as_echo "$ac_cv_x86_rdrand" >&6; }
+# Check if assembler supports rdseed opcode.
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for rdseed support in assembler" >&5
+$as_echo_n "checking for rdseed support in assembler... " >&6; }
+ if test "${ac_cv_x86_rdseed+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ac_cv_x86_rdseed=no
+ case "$target" in
+ i?86-*-* | \
+ x86_64-*-*)
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+asm("rdseed %eax");
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_x86_rdseed=yes
+else
+ ac_cv_x86_rdseed=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ esac
+
+fi
+
+ if test $ac_cv_x86_rdseed = yes; then
+
+$as_echo "#define _GLIBCXX_X86_RDSEED 1" >>confdefs.h
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_x86_rdseed" >&5
+$as_echo "$ac_cv_x86_rdseed" >&6; }
+
# This depends on GLIBCXX_ENABLE_SYMVERS and GLIBCXX_IS_NATIVE.
diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac
index dadd8827b49..2e3a1a98f33 100644
--- a/libstdc++-v3/configure.ac
+++ b/libstdc++-v3/configure.ac
@@ -417,6 +417,8 @@ GCC_CHECK_ASSEMBLER_HWCAP
# Check if assembler supports rdrand opcode.
GLIBCXX_CHECK_X86_RDRAND
+# Check if assembler supports rdseed opcode.
+GLIBCXX_CHECK_X86_RDSEED
# This depends on GLIBCXX_ENABLE_SYMVERS and GLIBCXX_IS_NATIVE.
GLIBCXX_CONFIGURE_TESTSUITE
diff --git a/libstdc++-v3/doc/html/manual/appendix_contributing.html b/libstdc++-v3/doc/html/manual/appendix_contributing.html
index ca8ae873b9e..7c7bd5a2593 100644
--- a/libstdc++-v3/doc/html/manual/appendix_contributing.html
+++ b/libstdc++-v3/doc/html/manual/appendix_contributing.html
@@ -26,7 +26,7 @@
organization. In the USA, this national standards
organization is
ANSI.
- (And if you've already registered with them you can buy
+ (And if you've already registered with them you can buy
the standard on-line.)
The library working group bugs, and known defects, can
diff --git a/libstdc++-v3/doc/html/manual/status.html b/libstdc++-v3/doc/html/manual/status.html
index 38400194eb3..398a9490a18 100644
--- a/libstdc++-v3/doc/html/manual/status.html
+++ b/libstdc++-v3/doc/html/manual/status.html
@@ -303,18 +303,38 @@ particular release.
minstd_rand0
.
26.5.6 [rand.device]
- The default token
argument to the
- random_device
constructor is
- "default"
. Other valid arguments are
- "/dev/random"
and "/dev/urandom"
,
- which determine the character special file to read random bytes from.
- The "default"
token will read bytes from a hardware
- RNG if available (currently this only supports the IA-32 RDRAND
- instruction) otherwise it is equivalent to
- "/dev/urandom"
.
+ The token
parameter of the
+ random_device
constructor can be used to select
+ a specific source of random bytes. The valid token values are shown
+ in the list below.
+ The default constructor uses the token "default"
.
+
"default"
-
+ Select the first available source from the other entries below.
+ This is the only token that is always valid.
+
"rand_s"
-
+ Use the MSVCRT
rand_s
function.
+ This token is only valid for mingw-w64 targets.
+ "rdseed"
, "rdrand"
or "rdrnd"
-
+ Use the IA-32
RDSEED
or RDRAND
+ instruction to read from an on-chip hardware random number generator.
+ These tokens are only valid for x86 and x86_64 targets when both
+ the assembler and CPU support the corresponding instruction.
+ "/dev/urandom"
, "/dev/random"
-
+ Use the named character special file to read random bytes from.
+ These tokens are only valid when the device files are present
+ and readable by the current user.
+
"mt19937"
, seed value-
+ When no source of nondeterministic random numbers is available a
+
mersenne_twister_engine
will be used.
+ An integer seed value can be used as the token and will be converted
+ to an unsigned long
using strtoul
.
+ These tokens are only valid when no other source of random bytes
+ is available.
+
An exception of type runtime_error
will be
- thrown if a random_device
object cannot open
- or read from the source of random bytes.
+ thrown if a random_device
object is constructed
+ with an invalid token, or if it cannot open or read from the source
+ of random bytes.
26.5.8.1 [rand.dist.general]
The algorithms used by the distributions should be documented here.
diff --git a/libstdc++-v3/doc/xml/manual/status_cxx2011.xml b/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
index 0fa4bc0dffe..9c25b8fd81f 100644
--- a/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
+++ b/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
@@ -2682,18 +2682,62 @@ particular release.
26.5.6 [rand.device]
- The default token
argument to the
- random_device constructor is
- "default". Other valid arguments are
- "/dev/random" and "/dev/urandom",
- which determine the character special file to read random bytes from.
- The "default" token will read bytes from a hardware
- RNG if available (currently this only supports the IA-32 RDRAND
- instruction) otherwise it is equivalent to
- "/dev/urandom".
+ The token
parameter of the
+ random_device constructor can be used to select
+ a specific source of random bytes. The valid token values are shown
+ in the list below.
+ The default constructor uses the token "default".
+
+
+ "default"
+
+ Select the first available source from the other entries below.
+ This is the only token that is always valid.
+
+
+
+ "rand_s"
+
+ Use the MSVCRT rand_s function.
+ This token is only valid for mingw-w64 targets.
+
+
+
+ "rdseed"
+ "rdrand" or "rdrnd"
+
+ Use the IA-32 RDSEED or RDRAND
+ instruction to read from an on-chip hardware random number generator.
+ These tokens are only valid for x86 and x86_64 targets when both
+ the assembler and CPU support the corresponding instruction.
+
+
+
+ "/dev/urandom"
+ "/dev/random"
+
+ Use the named character special file to read random bytes from.
+ These tokens are only valid when the device files are present
+ and readable by the current user.
+
+
+
+ "mt19937"
+ seed value
+
+ When no source of nondeterministic random numbers is available a
+ mersenne_twister_engine will be used.
+ An integer seed value can be used as the token and will be converted
+ to an unsigned long
using strtoul.
+ These tokens are only valid when no other source of random bytes
+ is available.
+
+
+
An exception of type runtime_error will be
- thrown if a random_device object cannot open
- or read from the source of random bytes.
+ thrown if a random_device object is constructed
+ with an invalid token, or if it cannot open or read from the source
+ of random bytes.
diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h
index 2b1df4cb59e..9c959d6dc84 100644
--- a/libstdc++-v3/include/bits/random.h
+++ b/libstdc++-v3/include/bits/random.h
@@ -1602,20 +1602,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// constructors, destructors and member functions
-#ifdef _GLIBCXX_USE_DEV_RANDOM
random_device() { _M_init("default"); }
explicit
random_device(const std::string& __token) { _M_init(__token); }
+#if defined _GLIBCXX_USE_DEV_RANDOM
~random_device()
{ _M_fini(); }
-#else
- random_device() { _M_init_pretr1("mt19937"); }
-
- explicit
- random_device(const std::string& __token)
- { _M_init_pretr1(__token); }
#endif
static constexpr result_type
@@ -1638,13 +1632,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
result_type
operator()()
- {
-#ifdef _GLIBCXX_USE_DEV_RANDOM
- return this->_M_getval();
-#else
- return this->_M_getval_pretr1();
-#endif
- }
+ { return this->_M_getval(); }
// No copy functions.
random_device(const random_device&) = delete;
@@ -1662,7 +1650,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
union
{
- void* _M_file;
+ struct
+ {
+ void* _M_file;
+ result_type (*_M_func)(void*);
+ int _M_fd;
+ };
mt19937 _M_mt;
};
};
diff --git a/libstdc++-v3/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc
index 1146d21c3f9..85cb2df4287 100644
--- a/libstdc++-v3/src/c++11/random.cc
+++ b/libstdc++-v3/src/c++11/random.cc
@@ -23,19 +23,30 @@
// .
#define _GLIBCXX_USE_CXX11_ABI 1
+#define _CRT_RAND_S // define this before including to get rand_s
+
#include
#ifdef _GLIBCXX_USE_C99_STDINT_TR1
#if defined __i386__ || defined __x86_64__
# include
+# ifdef _GLIBCXX_X86_RDRAND
+# define USE_RDRAND 1
+# endif
+# ifdef _GLIBCXX_X86_RDSEED
+# define USE_RDSEED 1
+# endif
#endif
#include
#include
-#ifdef _GLIBCXX_HAVE_UNISTD_H
+#if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H
# include
+# include
+// Use POSIX open, close, read etc. instead of ISO fopen, fclose, fread
+# define USE_POSIX_FILE_IO
#endif
#ifdef _GLIBCXX_HAVE_SYS_IOCTL_H
@@ -50,105 +61,273 @@
# include
#endif
+#ifdef _GLIBCXX_USE_CRT_RAND_S
+# include
+#endif
+
+#if defined USE_RDRAND || defined USE_RDSEED \
+ || defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM
+# pragma GCC poison _M_mt
+#else
+// Use the mt19937 member of the union, as in previous GCC releases.
+# define USE_MT19937 1
+#endif
+
namespace std _GLIBCXX_VISIBILITY(default)
{
namespace
{
- static unsigned long
- _M_strtoul(const std::string& __str)
- {
- unsigned long __ret = 5489UL;
- if (__str != "mt19937")
- {
- const char* __nptr = __str.c_str();
- char* __endptr;
- __ret = std::strtoul(__nptr, &__endptr, 0);
- if (*__nptr == '\0' || *__endptr != '\0')
- std::__throw_runtime_error(__N("random_device::_M_strtoul"
- "(const std::string&)"));
- }
- return __ret;
- }
-
-#if (defined __i386__ || defined __x86_64__) && defined _GLIBCXX_X86_RDRAND
+#if USE_RDRAND
unsigned int
__attribute__ ((target("rdrnd")))
- __x86_rdrand(void)
+ __x86_rdrand(void*)
{
unsigned int retries = 100;
unsigned int val;
while (__builtin_ia32_rdrand32_step(&val) == 0)
if (--retries == 0)
- std::__throw_runtime_error(__N("random_device::__x86_rdrand(void)"));
+ std::__throw_runtime_error(__N("random_device: rdrand failed"));
return val;
}
#endif
+
+#if USE_RDSEED
+ unsigned int
+ __attribute__ ((target("rdseed")))
+ __x86_rdseed(void*)
+ {
+ unsigned int retries = 100;
+ unsigned int val;
+
+ while (__builtin_ia32_rdseed_si_step(&val) == 0)
+ {
+ if (--retries == 0)
+ std::__throw_runtime_error(__N("random_device: rdseed failed"));
+ __builtin_ia32_pause();
+ }
+
+ return val;
+ }
+#endif
+
+#ifdef _GLIBCXX_USE_CRT_RAND_S
+ unsigned int
+ __winxp_rand_s(void*)
+ {
+ unsigned int val;
+ if (::rand_s(&val) != 0)
+ std::__throw_runtime_error(__N("random_device: rand_s failed"));
+ return val;
+ }
+#endif
}
void
random_device::_M_init(const std::string& token)
{
- const char *fname = token.c_str();
+#ifdef USE_MT19937
+ // If no real random device is supported then use the mt19937 engine.
+ _M_init_pretr1(token);
+ return;
+#else
+
+ _M_file = nullptr;
+ _M_func = nullptr;
+ _M_fd = -1;
+
+ const char* fname [[gnu::unused]] = nullptr;
+ bool default_token [[gnu::unused]] = false;
+
+ enum { rand_s, rdseed, rdrand, device_file } which;
if (token == "default")
{
-#if (defined __i386__ || defined __x86_64__) && defined _GLIBCXX_X86_RDRAND
+ default_token = true;
+ fname = "/dev/urandom";
+#if defined _GLIBCXX_USE_CRT_RAND_S
+ which = rand_s;
+#elif defined USE_RDSEED
+ which = rdseed;
+#elif defined USE_RDRAND
+ which = rdrand;
+#elif defined _GLIBCXX_USE_DEV_RANDOM
+ which = device_file;
+#else
+# error "either define USE_MT19937 above or set the default device here"
+#endif
+ }
+#ifdef USE_RDSEED
+ else if (token == "rdseed")
+ which = rdseed;
+#endif // USE_RDSEED
+#ifdef USE_RDRAND
+ else if (token == "rdrand" || token == "rdrnd")
+ which = rdrand;
+#endif // USE_RDRAND
+#ifdef _GLIBCXX_USE_CRT_RAND_S
+ else if (token == "rand_s")
+ which = rand_s;
+#endif // _GLIBCXX_USE_CRT_RAND_S
+#ifdef _GLIBCXX_USE_DEV_RANDOM
+ else if (token == "/dev/urandom" || token == "/dev/random")
+ {
+ fname = token.c_str();
+ which = device_file;
+ }
+#endif // _GLIBCXX_USE_DEV_RANDOM
+ else
+ std::__throw_runtime_error(
+ __N("random_device::random_device(const std::string&):"
+ " unsupported token"));
+
+ switch (which)
+ {
+#ifdef _GLIBCXX_USE_CRT_RAND_S
+ case rand_s:
+ {
+ _M_func = &__winxp_rand_s;
+ return;
+ }
+#endif // _GLIBCXX_USE_CRT_RAND_S
+#ifdef USE_RDSEED
+ case rdseed:
+ {
+ unsigned int eax, ebx, ecx, edx;
+ // Check availability of cpuid and, for now at least, also the
+ // CPU signature for Intel and AMD.
+ if (__get_cpuid_max(0, &ebx) > 0
+ && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx))
+ {
+ // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18]
+ __cpuid_count(7, 0, eax, ebx, ecx, edx);
+ if (ebx & bit_RDSEED)
+ {
+ _M_func = &__x86_rdseed;
+ return;
+ }
+ }
+ // If rdseed was explicitly requested then we're done here.
+ if (!default_token)
+ break;
+ // Otherwise fall through to try the next available option.
+ [[gnu::fallthrough]];
+ }
+#endif // USE_RDSEED
+#ifdef USE_RDRAND
+ case rdrand:
+ {
unsigned int eax, ebx, ecx, edx;
// Check availability of cpuid and, for now at least, also the
- // CPU signature for Intel's
- if (__get_cpuid_max(0, &ebx) > 0 && ebx == signature_INTEL_ebx)
+ // CPU signature for Intel and AMD.
+ if (__get_cpuid_max(0, &ebx) > 0
+ && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx))
{
+ // CPUID.01H:ECX.RDRAND[bit 30]
__cpuid(1, eax, ebx, ecx, edx);
if (ecx & bit_RDRND)
{
- _M_file = nullptr;
+ _M_func = &__x86_rdrand;
return;
}
}
-#endif
-
- fname = "/dev/urandom";
+ // If rdrand was explicitly requested then we're done here.
+ if (!default_token)
+ break;
+ // Otherwise fall through to try the next available option.
+ [[gnu::fallthrough]];
}
- else if (token != "/dev/urandom" && token != "/dev/random")
- fail:
- std::__throw_runtime_error(__N("random_device::"
- "random_device(const std::string&)"));
-
- _M_file = static_cast(std::fopen(fname, "rb"));
- if (!_M_file)
- goto fail;
+#endif // USE_RDRAND
+#ifdef _GLIBCXX_USE_DEV_RANDOM
+ case device_file:
+ {
+#ifdef USE_POSIX_FILE_IO
+ _M_fd = ::open(fname, O_RDONLY);
+ if (_M_fd != -1)
+ {
+ // Set _M_file to non-null so that _M_fini() will do clean up.
+ _M_file = &_M_fd;
+ return;
+ }
+#else // USE_POSIX_FILE_IO
+ _M_file = static_cast(std::fopen(fname, "rb"));
+ if (_M_file)
+ return;
+#endif // USE_POSIX_FILE_IO
+ [[gnu::fallthrough]];
+ }
+#endif // _GLIBCXX_USE_DEV_RANDOM
+ default:
+ { }
+ }
+ std::__throw_runtime_error(
+ __N("random_device::random_device(const std::string&):"
+ " device not available"));
+#endif // USE_MT19937
}
+ // This function is called by _M_init for targets that use mt19937 for
+ // randomness, and by code compiled against old releases of libstdc++.
void
random_device::_M_init_pretr1(const std::string& token)
{
- _M_mt.seed(_M_strtoul(token));
+#ifdef USE_MT19937
+ unsigned long seed = 5489UL;
+ if (token != "default" && token != "mt19937")
+ {
+ const char* nptr = token.c_str();
+ char* endptr;
+ seed = std::strtoul(nptr, &endptr, 0);
+ if (*nptr == '\0' || *endptr != '\0')
+ std::__throw_runtime_error(__N("random_device::_M_init_pretr1"
+ "(const std::string&)"));
+ }
+ _M_mt.seed(seed);
+#else
+ // Convert old default token "mt19937" or numeric seed tokens to "default".
+ if (token == "mt19937" || isdigit((unsigned char)token[0]))
+ _M_init("default");
+ else
+ _M_init(token);
+#endif
}
void
random_device::_M_fini()
{
- if (_M_file)
- std::fclose(static_cast(_M_file));
+ // _M_file == nullptr means no resources to free.
+ if (!_M_file)
+ return;
+
+#ifdef USE_POSIX_FILE_IO
+ ::close(_M_fd);
+ _M_fd = -1;
+#else
+ std::fclose(static_cast(_M_file));
+#endif
+ _M_file = nullptr;
}
random_device::result_type
random_device::_M_getval()
{
-#if (defined __i386__ || defined __x86_64__) && defined _GLIBCXX_X86_RDRAND
- if (!_M_file)
- return __x86_rdrand();
+#ifdef USE_MT19937
+ return _M_mt();
+#else
+
+#if defined USE_RDRAND || defined USE_RDSEED || defined _GLIBCXX_USE_CRT_RAND_S
+ if (_M_func)
+ return _M_func(nullptr);
#endif
- result_type __ret;
- void* p = &__ret;
+ result_type ret;
+ void* p = &ret;
size_t n = sizeof(result_type);
-#ifdef _GLIBCXX_HAVE_UNISTD_H
+#ifdef USE_POSIX_FILE_IO
do
{
- const int e = read(fileno(static_cast(_M_file)), p, n);
+ const int e = ::read(_M_fd, p, n);
if (e > 0)
{
n -= e;
@@ -158,34 +337,40 @@ namespace std _GLIBCXX_VISIBILITY(default)
__throw_runtime_error(__N("random_device could not be read"));
}
while (n > 0);
-#else
+#else // USE_POSIX_FILE_IO
const size_t e = std::fread(p, n, 1, static_cast(_M_file));
if (e != 1)
__throw_runtime_error(__N("random_device could not be read"));
-#endif
+#endif // USE_POSIX_FILE_IO
- return __ret;
+ return ret;
+#endif // USE_MT19937
}
+ // Only called by code compiled against old releases of libstdc++.
+ // Forward the call to _M_getval() and let it decide what to do.
random_device::result_type
random_device::_M_getval_pretr1()
- {
- return _M_mt();
- }
+ { return _M_getval(); }
double
random_device::_M_getentropy() const noexcept
{
-#if defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT
+#if defined _GLIBCXX_USE_DEV_RANDOM \
+ && defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT
if (!_M_file)
return 0.0;
- const int fd = fileno(static_cast(_M_file));
+#ifdef USE_POSIX_FILE_IO
+ const int fd = _M_fd;
+#else
+ const int fd = ::fileno(static_cast(_M_file));
+#endif
if (fd < 0)
return 0.0;
int ent;
- if (ioctl(fd, RNDGETENTCNT, &ent) < 0)
+ if (::ioctl(fd, RNDGETENTCNT, &ent) < 0)
return 0.0;
if (ent < 0)
@@ -198,9 +383,10 @@ namespace std _GLIBCXX_VISIBILITY(default)
return static_cast(ent);
#else
return 0.0;
-#endif
+#endif // _GLIBCXX_USE_DEV_RANDOM && _GLIBCXX_HAVE_SYS_IOCTL_H && RNDGETENTCNT
}
+#ifdef USE_MT19937
template class mersenne_twister_engine<
uint_fast32_t,
32, 624, 397, 31,
@@ -208,5 +394,6 @@ namespace std _GLIBCXX_VISIBILITY(default)
0xffffffffUL, 7,
0x9d2c5680UL, 15,
0xefc60000UL, 18, 1812433253UL>;
+#endif // USE_MT19937
}
-#endif
+#endif // _GLIBCXX_USE_C99_STDINT_TR1
diff --git a/libstdc++-v3/testsuite/26_numerics/random/random_device/85494.cc b/libstdc++-v3/testsuite/26_numerics/random/random_device/85494.cc
new file mode 100644
index 00000000000..2670ad7225a
--- /dev/null
+++ b/libstdc++-v3/testsuite/26_numerics/random/random_device/85494.cc
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// .
+
+// { dg-do run { target c++11 } }
+// { dg-require-effective-target random_device }
+
+#include
+#include
+
+void
+test01()
+{
+ unsigned v1[3], v2[3];
+ std::random_device d1, d2;
+ for (auto& v : v1)
+ v = d1();
+ for (auto& v : v2)
+ v = d2();
+ VERIFY (v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] );
+}
+
+int
+main()
+{
+ test01();
+}
--
2.30.2