This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH]std::random_device for win32 (MinGW) ABI implementation


On 12/10/18 17:45 +0000, sotrdg sotrdg wrote:
The implementation is on the GitHub.

https://github.com/euloanty/mingw-std-random_device/blob/master/random_device_gcc_withcxx11abi/random.cc


This implementation does several changes towards the original random.cc.


 1.  Type Erasure Abstractions for various random_devices so we can choose any of them to use if necessary.
 2.  Solve security issue of setvbuf(fp,nullptr,_IONBF,0); In order to avoid seeds leaking to other processes after releasing the buffer of std::FILE*.
 3.  For amd64 platform, random_device will prefer using rdseed instruction instead of rdrand instruction
 4.  For Windows, random_device will get seeds from RtlGenRandom and buffer it.
 5.  Legacy support for mt19937.
I’ve put it on the GitHub for anyone.

Thanks for this, although I'm afraid we can't use it without some
additional steps being taken, see https://gcc.gnu.org/contribute.html#legal

However, as I mentioned at
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85494#c4 I've already
been working on an implementation that uses RtlGenRandom. My version
is attached. This implementation allows constructing a
std::random_device with the following tokens (where supported):

"default" - choose the first supported "rtlgenrandom" - use Windows RtlGenRandom
"rdrand" or "rdrnd" - use RDRAND instruction
"rdseed" - use RDSEED instruction
"/dev/urandom" or "/dev/random" - use specified device file

This should be an ABI compatible change, so that existing MinGW
applications don't need to be recompiled, they just need to use the
new libstdc++-6.dll to get the new implementation (although they won't
get the secure zeroing out of memory without a recompile).

I've only done minimal testing using a mingw-w64 cross-compile and
executing under Wine, but it seems to work well.

commit c5d984ffc82c0509b012c011d055e6d98ffc261f
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Jul 4 15:42:36 2018 +0100

    WIP improve std::random_device (TODO: new _V2::random_device without mt19337 in union!)

diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index 82a25e5f2f1..1581a7f348c 100644
--- a/libstdc++-v3/acinclude.m4
+++ b/libstdc++-v3/acinclude.m4
@@ -3978,6 +3978,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.h.in b/libstdc++-v3/config.h.in
index fa69b7a0895..35a8b26976e 100644
--- a/libstdc++-v3/config.h.in
+++ b/libstdc++-v3/config.h.in
@@ -1020,6 +1020,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/configure b/libstdc++-v3/configure
index 12a6d4cf0ef..e683c12915d 100755
--- a/libstdc++-v3/configure
+++ b/libstdc++-v3/configure
@@ -79554,6 +79554,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 dbb7dbc137c..3e4de8bd9f6 100644
--- a/libstdc++-v3/configure.ac
+++ b/libstdc++-v3/configure.ac
@@ -411,6 +411,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/include/bits/random.h b/libstdc++-v3/include/bits/random.h
index 5e994aa8836..cdcadeee859 100644
--- a/libstdc++-v3/include/bits/random.h
+++ b/libstdc++-v3/include/bits/random.h
@@ -1602,7 +1602,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     // constructors, destructors and member functions
 
-#ifdef _GLIBCXX_USE_DEV_RANDOM
+#if defined _GLIBCXX_USE_DEV_RANDOM || defined __WINNT__
     random_device() { _M_init("default"); }
 
     explicit
@@ -1611,7 +1611,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     ~random_device()
     { _M_fini(); }
 #else
-    random_device() { _M_init("mt19937"); }
+    random_device() { _M_init_pretr1("mt19937"); }
 
     explicit
     random_device(const std::string& __token)
@@ -1639,7 +1639,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     result_type
     operator()()
     {
-#ifdef _GLIBCXX_USE_DEV_RANDOM
+#if defined _GLIBCXX_USE_DEV_RANDOM
       return this->_M_getval();
 #else
       return this->_M_getval_pretr1();
@@ -1662,7 +1662,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     union
     {
-      void*      _M_file;
+      struct {
+	  void*      _M_file;
+	  result_type (*_M_func)(void*);
+	  char _M_buf[sizeof(result_type) * 128 + sizeof(result_type*)];
+      };
       mt19937    _M_mt;
     };
   };
diff --git a/libstdc++-v3/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc
index 19daf6d6319..c034c4f84fc 100644
--- a/libstdc++-v3/src/c++11/random.cc
+++ b/libstdc++-v3/src/c++11/random.cc
@@ -29,6 +29,12 @@
 
 #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>
@@ -50,30 +56,21 @@
 # include <linux/random.h>
 #endif
 
+#ifdef __WINNT__
+# include <array>
+# include <windows.h>
+# include <ntsecapi.h>
+# define USE_RTLGENRANDOM
+#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;
@@ -85,16 +82,76 @@ namespace std _GLIBCXX_VISIBILITY(default)
       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::__x86_rdseed(void)"));
+
+      return val;
+    }
+#endif
+
+#ifdef USE_RTLGENRANDOM
+    struct rtlgenrandom_type
+    {
+      using result_type = random_device::result_type;
+      using array = std::array<result_type, 128>;
+      array data;
+      array::const_iterator next = data.end();
+
+      static result_type _S_gen(void* p)
+      { return static_cast<rtlgenrandom_type*>(p)->_M_gen(); }
+
+      result_type _M_gen()
+      {
+	if (next == data.end())
+	  {
+	    if (!RtlGenRandom(data.data(), data.size() * sizeof(result_type)))
+	      __throw_runtime_error(__N("random_device got an error from"
+					" RtlGenRandom"));
+	    next = data.begin();
+	  }
+	return *next++;
+      }
+
+      ~rtlgenrandom_type()
+      { SecureZeroMemory(data.data(), data.size() * sizeof(result_type)); }
+    };
+#endif
   }
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-label"
   void
   random_device::_M_init(const std::string& token)
   {
-    const char *fname = token.c_str();
+    const char* fname = nullptr;
 
     if (token == "default")
       {
-#if (defined __i386__ || defined __x86_64__) && defined _GLIBCXX_X86_RDRAND
+#if defined USE_RTLGENRANDOM
+	goto use_rtlgenrandom;
+#elif defined USE_RDRAND
+	goto use_rdrand;
+#elif defined USE_RDSEED
+	goto use_rdseed;
+#else
+	fname = "/dev/urandom";
+	goto use_device_file;
+#endif
+      }
+#ifdef USE_RDRAND
+    else if (token == "rdrand" || token == "rdrnd")
+      {
+use_rdrand:
 	unsigned int eax, ebx, ecx, edx;
 	// Check availability of cpuid and, for now at least, also the
 	// CPU signature for Intel's
@@ -103,43 +160,106 @@ namespace std _GLIBCXX_VISIBILITY(default)
 	    __cpuid(1, eax, ebx, ecx, edx);
 	    if (ecx & bit_RDRND)
 	      {
+		_M_func = &__x86_rdrand;
 		_M_file = nullptr;
 		return;
 	      }
 	  }
+      }
 #endif
-
-	fname = "/dev/urandom";
+#ifdef USE_RDSEED
+    else if (token == "rdseed")
+      {
+use_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)
+	  {
+	    __cpuid(1, eax, ebx, ecx, edx);
+	    if (ebx & bit_RDSEED)
+	      {
+		_M_func = &__x86_rdseed;
+		_M_file = nullptr;
+		return;
+	      }
+	  }
       }
-    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
+#ifdef USE_RTLGENRANDOM
+    else if (token == "rtlgenrandom")
+      {
+use_rtlgenrandom:
+	_M_file = new (&_M_buf) rtlgenrandom_type;
+	_M_func = &rtlgenrandom_type::_S_gen;
+	return;
+      }
+#endif
+    else if (token == "/dev/urandom" || token == "/dev/random")
+      {
+	fname = token.c_str();
+use_device_file:
+	_M_file = static_cast<void*>(std::fopen(fname, "rb"));
+	if (_M_file)
+	  return;
+      }
+    else
+      std::__throw_runtime_error(
+	  __N("random_device::random_device(const std::string&):"
+	      " device not recognized"));
+
+    std::__throw_runtime_error(
+	__N("random_device::random_device(const std::string&):"
+	    " device not available"));
   }
+#pragma GCC diagnostic pop
 
   void
-  random_device::_M_init_pretr1(const std::string& token)
+  random_device::_M_init_pretr1(const std::string& token [[gnu::unused]])
   {
-    _M_mt.seed(_M_strtoul(token));
+#ifdef USE_RTLGENRANDOM
+    _M_init("rtlgenrandom");
+#else
+    unsigned long seed = 5489UL;
+    if (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_strtoul"
+					 "(const std::string&)"));
+      }
+    _M_mt.seed(seed);
+#endif
   }
 
   void
   random_device::_M_fini()
   {
-    if (_M_file)
-      std::fclose(static_cast<FILE*>(_M_file));
+    if (!_M_file)
+      return;
+
+#ifdef USE_RTLGENRANDOM
+    if (_M_func)
+      {
+	static_cast<rtlgenrandom_type*>(_M_file)->~rtlgenrandom_type();
+	_M_func = nullptr;
+	_M_file = nullptr;
+	return;
+      }
+#endif
+
+    std::fclose(static_cast<FILE*>(_M_file));
+    _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();
+#if defined USE_RDRAND || defined USE_RDSEED || defined USE_RTLGENRANDOM
+    if (_M_func)
+      return _M_func(_M_file);
 #endif
 
     result_type __ret;
@@ -170,12 +290,21 @@ namespace std _GLIBCXX_VISIBILITY(default)
   random_device::result_type
   random_device::_M_getval_pretr1()
   {
+#ifdef USE_RTLGENRANDOM
+    return _M_getval();
+#else
     return _M_mt();
+#endif
   }
 
   double
   random_device::_M_getentropy() const noexcept
   {
+#if defined USE_RDRAND || defined USE_RDSEED || defined USE_RTLGENRANDOM
+    if (_M_func)
+      return 0.0;
+#endif
+
 #if defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT
     if (!_M_file)
       return 0.0;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]