diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2019-05-29 15:45:35 +0100 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2019-05-29 15:45:35 +0100 |
commit | b0c0d878a8b5bf39dbea4c192fed26d340524439 (patch) | |
tree | 0fe0e765cf8c296b2c62be34aa53081dbb7e80a1 | |
parent | 0617e23c9531373d3b232152c0d81a2c707858d9 (diff) | |
download | gcc-b0c0d878a8b5bf39dbea4c192fed26d340524439.zip gcc-b0c0d878a8b5bf39dbea4c192fed26d340524439.tar.gz gcc-b0c0d878a8b5bf39dbea4c192fed26d340524439.tar.bz2 |
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 <bits/random.h> 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
-rw-r--r-- | libstdc++-v3/ChangeLog | 43 | ||||
-rw-r--r-- | libstdc++-v3/acinclude.m4 | 20 | ||||
-rw-r--r-- | libstdc++-v3/config.h.in | 3 | ||||
-rw-r--r-- | libstdc++-v3/config/os/mingw32-w64/os_defines.h | 2 | ||||
-rwxr-xr-x | libstdc++-v3/configure | 41 | ||||
-rw-r--r-- | libstdc++-v3/configure.ac | 2 | ||||
-rw-r--r-- | libstdc++-v3/doc/html/manual/appendix_contributing.html | 2 | ||||
-rw-r--r-- | libstdc++-v3/doc/html/manual/status.html | 42 | ||||
-rw-r--r-- | libstdc++-v3/doc/xml/manual/status_cxx2011.xml | 66 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/random.h | 23 | ||||
-rw-r--r-- | libstdc++-v3/src/c++11/random.cc | 301 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/26_numerics/random/random_device/85494.cc | 40 |
12 files changed, 490 insertions, 95 deletions
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 31f7766..a01b54e 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,46 @@ +2019-05-29 Jonathan Wakely <jwakely@redhat.com> + + 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 <jwakely@redhat.com> PR libstdc++/90634 diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 84258d8..19e9f14 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -4054,6 +4054,26 @@ AC_DEFUN([GLIBCXX_CHECK_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 <sys/sysinfo.h>, and define _GLIBCXX_USE_GET_NPROCS. dnl AC_DEFUN([GLIBCXX_CHECK_GET_NPROCS], [ diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in index 3a6f180..99286e6 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 6c19d39..418c6f5 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 7d134a0..7c45c34 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 dadd882..2e3a1a9 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 ca8ae87..7c7bd5a 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 <a class="link" href="https://www.ansi.org" target="_top">ANSI</a>. - (And if you've already registered with them you can <a class="link" href="https://webstore.ansi.org/RecordDetail.aspx?sku=ISO%2fIEC+14882%3a2014" target="_top">buy + (And if you've already registered with them you can <a class="link" href="https://webstore.ansi.org/Standards/ISO/ISOIEC148822014" target="_top">buy the standard on-line</a>.) </p></li><li class="listitem"><p> 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 3840019..398a9490 100644 --- a/libstdc++-v3/doc/html/manual/status.html +++ b/libstdc++-v3/doc/html/manual/status.html @@ -303,18 +303,38 @@ particular release. <code class="classname">minstd_rand0</code>. </p><p> <span class="emphasis"><em> 26.5.6 [rand.device] </em></span> - The default <code class="code">token</code> argument to the - <code class="classname">random_device</code> constructor is - <code class="literal">"default"</code>. Other valid arguments are - <code class="literal">"/dev/random"</code> and <code class="literal">"/dev/urandom"</code>, - which determine the character special file to read random bytes from. - The <code class="literal">"default"</code> token will read bytes from a hardware - RNG if available (currently this only supports the IA-32 RDRAND - instruction) otherwise it is equivalent to - <code class="literal">"/dev/urandom"</code>. + The <code class="code">token</code> parameter of the + <code class="classname">random_device</code> 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 <code class="literal">"default"</code>. + </p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="literal">"default"</code></span></dt><dd> + Select the first available source from the other entries below. + This is the only token that is always valid. + </dd><dt><span class="term"><code class="literal">"rand_s"</code></span></dt><dd> + Use the MSVCRT <code class="function">rand_s</code> function. + This token is only valid for mingw-w64 targets. + </dd><dt><span class="term"><code class="literal">"rdseed"</code>, </span><span class="term"><code class="literal">"rdrand"</code> or <code class="literal">"rdrnd"</code></span></dt><dd> + Use the IA-32 <code class="literal">RDSEED</code> or <code class="literal">RDRAND</code> + 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. + </dd><dt><span class="term"><code class="literal">"/dev/urandom"</code>, </span><span class="term"><code class="literal">"/dev/random"</code></span></dt><dd> + 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. + </dd><dt><span class="term"><code class="literal">"mt19937"</code>, </span><span class="term">seed value</span></dt><dd> + When no source of nondeterministic random numbers is available a + <code class="classname">mersenne_twister_engine</code> will be used. + An integer seed value can be used as the token and will be converted + to an <code class="code">unsigned long</code> using <code class="function">strtoul</code>. + These tokens are only valid when no other source of random bytes + is available. + </dd></dl></div><p> An exception of type <code class="classname">runtime_error</code> will be - thrown if a <code class="classname">random_device</code> object cannot open - or read from the source of random bytes. + thrown if a <code class="classname">random_device</code> object is constructed + with an invalid token, or if it cannot open or read from the source + of random bytes. </p><p> <span class="emphasis"><em>26.5.8.1 [rand.dist.general]</em></span> 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 0fa4bc0..9c25b8f 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. <para> <emphasis> 26.5.6 [rand.device] </emphasis> - The default <code>token</code> argument to the - <classname>random_device</classname> constructor is - <literal>"default"</literal>. Other valid arguments are - <literal>"/dev/random"</literal> and <literal>"/dev/urandom"</literal>, - which determine the character special file to read random bytes from. - The <literal>"default"</literal> token will read bytes from a hardware - RNG if available (currently this only supports the IA-32 RDRAND - instruction) otherwise it is equivalent to - <literal>"/dev/urandom"</literal>. + The <code>token</code> parameter of the + <classname>random_device</classname> 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 <literal>"default"</literal>. + <variablelist> + <varlistentry> + <term><literal>"default"</literal></term> + <listitem> + Select the first available source from the other entries below. + This is the only token that is always valid. + </listitem> + </varlistentry> + <varlistentry> + <term><literal>"rand_s"</literal></term> + <listitem> + Use the MSVCRT <function>rand_s</function> function. + This token is only valid for mingw-w64 targets. + </listitem> + </varlistentry> + <varlistentry> + <term><literal>"rdseed"</literal></term> + <term><literal>"rdrand"</literal> or <literal>"rdrnd"</literal></term> + <listitem> + Use the IA-32 <literal>RDSEED</literal> or <literal>RDRAND</literal> + 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. + </listitem> + </varlistentry> + <varlistentry> + <term><literal>"/dev/urandom"</literal></term> + <term><literal>"/dev/random"</literal></term> + <listitem> + 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. + </listitem> + </varlistentry> + <varlistentry> + <term><literal>"mt19937"</literal></term> + <term>seed value</term> + <listitem> + When no source of nondeterministic random numbers is available a + <classname>mersenne_twister_engine</classname> will be used. + An integer seed value can be used as the token and will be converted + to an <code>unsigned long</code> using <function>strtoul</function>. + These tokens are only valid when no other source of random bytes + is available. + </listitem> + </varlistentry> + </variablelist> An exception of type <classname>runtime_error</classname> will be - thrown if a <classname>random_device</classname> object cannot open - or read from the source of random bytes. + thrown if a <classname>random_device</classname> object is constructed + with an invalid token, or if it cannot open or read from the source + of random bytes. </para> <para> diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h index 2b1df4c..9c959d6 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 1146d21..85cb2df 100644 --- a/libstdc++-v3/src/c++11/random.cc +++ b/libstdc++-v3/src/c++11/random.cc @@ -23,19 +23,30 @@ // <http://www.gnu.org/licenses/>. #define _GLIBCXX_USE_CXX11_ABI 1 +#define _CRT_RAND_S // define this before including <stdlib.h> to get rand_s + #include <random> #ifdef _GLIBCXX_USE_C99_STDINT_TR1 #if defined __i386__ || defined __x86_64__ # include <cpuid.h> +# ifdef _GLIBCXX_X86_RDRAND +# define USE_RDRAND 1 +# endif +# ifdef _GLIBCXX_X86_RDSEED +# define USE_RDSEED 1 +# endif #endif #include <cerrno> #include <cstdio> -#ifdef _GLIBCXX_HAVE_UNISTD_H +#if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H # include <unistd.h> +# include <fcntl.h> +// 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 <linux/random.h> #endif +#ifdef _GLIBCXX_USE_CRT_RAND_S +# include <stdlib.h> +#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<void*>(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<void*>(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<FILE*>(_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<FILE*>(_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<FILE*>(_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<FILE*>(_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<FILE*>(_M_file)); +#ifdef USE_POSIX_FILE_IO + const int fd = _M_fd; +#else + const int fd = ::fileno(static_cast<FILE*>(_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<double>(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 0000000..2670ad7 --- /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 +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target c++11 } } +// { dg-require-effective-target random_device } + +#include <random> +#include <testsuite_hooks.h> + +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(); +} |