diff mbox series

PR libstdc++/85494 use rdseed and rand_s in std::random_device

Message ID 20190529144517.GA9078@redhat.com
State New
Headers show
Series PR libstdc++/85494 use rdseed and rand_s in std::random_device | expand

Commit Message

Jonathan Wakely May 29, 2019, 2:45 p.m. UTC
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.

Tested x86_64-linux, powerpc64le-linux and x86_64-w64-ming32.

Committed to trunk.
commit dc337c84a7db52035f6b6efb87338e950bb7490e
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue May 28 15:01:08 2019 +0100

    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.

Comments

Jonathan Wakely May 29, 2019, 10:03 p.m. UTC | #1
On 29/05/19 15:45 +0100, Jonathan Wakely wrote:
>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.

This fixes a test that started to fail in mingw.

Tested x86_64-linux, powerpc64le-linux and x86_64-w64-ming32.

Committed to trunk.
Jonathan Wakely May 30, 2019, 7:50 p.m. UTC | #2
On 29/05/19 23:03 +0100, Jonathan Wakely wrote:
>On 29/05/19 15:45 +0100, Jonathan Wakely wrote:
>>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.
>
>This fixes a test that started to fail in mingw.

I forgot that the random_device::_M_init member is duplicated in
src/c++11/cow-string-inst.cc for the old strings, so tests using the
old string now fail.

I'll try to find a better way to define those member functions for old
strings.
Szabolcs Nagy May 31, 2019, 9:26 a.m. UTC | #3
On 29/05/2019 23:03, Jonathan Wakely wrote:
> This fixes a test that started to fail in mingw.
> 
> Tested x86_64-linux, powerpc64le-linux and x86_64-w64-ming32.
> 
> Committed to trunk.

note that the updated test fails on baremetal

FAIL: 26_numerics/random/random_device/cons/token.cc execution test

26_numerics/random/random_device/cons/token.cc:67: void test03(): Assertion 'count != 0' failed.

(if i add "default" to the token list in test03 then it passes)
Jonathan Wakely May 31, 2019, 9:46 a.m. UTC | #4
On 31/05/19 09:26 +0000, Szabolcs Nagy wrote:
>On 29/05/2019 23:03, Jonathan Wakely wrote:
>> This fixes a test that started to fail in mingw.
>>
>> Tested x86_64-linux, powerpc64le-linux and x86_64-w64-ming32.
>>
>> Committed to trunk.
>
>note that the updated test fails on baremetal
>
>FAIL: 26_numerics/random/random_device/cons/token.cc execution test
>
>26_numerics/random/random_device/cons/token.cc:67: void test03(): Assertion 'count != 0' failed.
>
>(if i add "default" to the token list in test03 then it passes)

Hmm, then I messed something up. If the "default" token works then one
of the others should too (I would expect that to be "mt19937" for bare
metal).

I'll take a look, thanks.
Jonathan Wakely May 31, 2019, 9:47 a.m. UTC | #5
On 31/05/19 10:46 +0100, Jonathan Wakely wrote:
>On 31/05/19 09:26 +0000, Szabolcs Nagy wrote:
>>On 29/05/2019 23:03, Jonathan Wakely wrote:
>>>This fixes a test that started to fail in mingw.
>>>
>>>Tested x86_64-linux, powerpc64le-linux and x86_64-w64-ming32.
>>>
>>>Committed to trunk.
>>
>>note that the updated test fails on baremetal
>>
>>FAIL: 26_numerics/random/random_device/cons/token.cc execution test
>>
>>26_numerics/random/random_device/cons/token.cc:67: void test03(): Assertion 'count != 0' failed.
>>
>>(if i add "default" to the token list in test03 then it passes)
>
>Hmm, then I messed something up. If the "default" token works then one
>of the others should too (I would expect that to be "mt19937" for bare
>metal).
>
>I'll take a look, thanks.


Doh:

--- a/libstdc++-v3/testsuite/26_numerics/random/random_device/cons/token.cc
+++ b/libstdc++-v3/testsuite/26_numerics/random/random_device/cons/token.cc
@@ -51,7 +51,7 @@ test03()
 {
   // At least one of these tokens should be valid.
   const std::string tokens[] = {
-    "rdseed", "rdrand", "rand_s", "/dev/urandom", "/dev/random", "mt19337"
+    "rdseed", "rdrand", "rand_s", "/dev/urandom", "/dev/random", "mt19937"
   };
   int count = 0;
   for (const std::string& token : tokens)
Jonathan Wakely May 31, 2019, 10:34 a.m. UTC | #6
On 31/05/19 10:47 +0100, Jonathan Wakely wrote:
>On 31/05/19 10:46 +0100, Jonathan Wakely wrote:
>>On 31/05/19 09:26 +0000, Szabolcs Nagy wrote:
>>>On 29/05/2019 23:03, Jonathan Wakely wrote:
>>>>This fixes a test that started to fail in mingw.
>>>>
>>>>Tested x86_64-linux, powerpc64le-linux and x86_64-w64-ming32.
>>>>
>>>>Committed to trunk.
>>>
>>>note that the updated test fails on baremetal
>>>
>>>FAIL: 26_numerics/random/random_device/cons/token.cc execution test
>>>
>>>26_numerics/random/random_device/cons/token.cc:67: void test03(): Assertion 'count != 0' failed.
>>>
>>>(if i add "default" to the token list in test03 then it passes)
>>
>>Hmm, then I messed something up. If the "default" token works then one
>>of the others should too (I would expect that to be "mt19937" for bare
>>metal).
>>
>>I'll take a look, thanks.
>
>
>Doh:
>
>--- a/libstdc++-v3/testsuite/26_numerics/random/random_device/cons/token.cc
>+++ b/libstdc++-v3/testsuite/26_numerics/random/random_device/cons/token.cc
>@@ -51,7 +51,7 @@ test03()
>{
>  // At least one of these tokens should be valid.
>  const std::string tokens[] = {
>-    "rdseed", "rdrand", "rand_s", "/dev/urandom", "/dev/random", "mt19337"
>+    "rdseed", "rdrand", "rand_s", "/dev/urandom", "/dev/random", "mt19937"
>  };
>  int count = 0;
>  for (const std::string& token : tokens)


Here's what I've committed to fix the COW string failures and the
baremetal failure. Tested powerpc64le-linux and x86_64-linux, old and
new ABIs.
diff mbox series

Patch

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 <sys/sysinfo.h>, and define _GLIBCXX_USE_GET_NPROCS.
 dnl
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.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/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.
 
    <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 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 @@ 
 // <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'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(1, eax, ebx, ecx, edx);
-	    if (ecx & bit_RDRND)
+	    // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18]
+	    __cpuid_count(7, 0, eax, ebx, ecx, edx);
+	    if (ebx & bit_RDSEED)
 	      {
-		_M_file = nullptr;
+		_M_func = &__x86_rdseed;
 		return;
 	      }
 	  }
-#endif
-
-	fname = "/dev/urandom";
+	// 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]];
       }
-    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_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 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_func = &__x86_rdrand;
+		return;
+	      }
+	  }
+	// 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]];
+      }
+#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 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
+// <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();
+}