]> mj.ucw.cz Git - libucw.git/commitdiff
Random: Detect and use getrandom() to seed fastrand.
authorPavel Charvat <pchar@ucw.cz>
Tue, 22 Mar 2022 15:47:06 +0000 (15:47 +0000)
committerPavel Charvat <pchar@ucw.cz>
Tue, 22 Mar 2022 18:06:59 +0000 (18:06 +0000)
ucw/Makefile
ucw/default.cfg
ucw/perl/UCW/Configure/LibUCW.pm
ucw/random-fast.c
ucw/random-strong.c [new file with mode: 0644]
ucw/random.h

index 6fdb641fad81249107fffac5fd2c5ad5e31a6d70..87f328c376443c8938292eb94acd8447ac9c2f88 100644 (file)
@@ -22,7 +22,7 @@ LIBUCW_MODS= \
        char-cat char-upper char-lower unicode varint stkstring \
        wildmatch regex \
        prime primetable \
-       random-legacy random-fast \
+       random-legacy random-fast random-strong \
        time-stamp time-timer time-conf \
        bit-ffs bit-fls bit-array \
        url \
index 3d4b48818fec4499860fd2bdc28d123869017309..a0441b0fb295a052b461c57d122d1bf15a8f87b7 100644 (file)
@@ -1,5 +1,6 @@
 # Configuration variables of the UCW library and their default values
 # (c) 2005--2015 Martin Mares <mj@ucw.cz>
+# (c) 2020 Pavel Charvat <pchar@ucw.cz>
 
 # Version of the whole package
 Set("UCW_VERSION" => "6.5.13");
@@ -65,6 +66,9 @@ Set("CONFIG_UCW_FB_DIRECT");
 # Use epoll (needs support in libc and kernel, default: auto-detect)
 # Set("CONFIG_UCW_EPOLL");
 
+# Use getrandom (needs support in libc and kernel, default: auto-detect)
+# Set("CONFIG_UCW_GETRANDOM");
+
 # Use monotonic clock (default: yes on Linux, no elsewhere)
 # Set("CONFIG_UCW_MONOTONIC_CLOCK");
 
index 7feef1e29dbc2c0faeb3f96d113868c058d56123..4bf8adbd68f84f69eaa8bce80320233385b99e50 100644 (file)
@@ -2,6 +2,7 @@
 # (c) 2005--2012 Martin Mares <mj@ucw.cz>
 # (c) 2006 Robert Spalek <robert@ucw.cz>
 # (c) 2008 Michal Vaner <vorner@ucw.cz>
+# (c) 2020 Pavel Charvat <pchar@ucw.cz>
 
 package UCW::Configure::LibUCW;
 use UCW::Configure;
@@ -68,6 +69,19 @@ int main(void)
 FINIS
 });
 
+# Detect if we have the getrandom() syscall
+TestBool("CONFIG_UCW_GETRANDOM", "Checking for getrandom", sub {
+       return UCW::Configure::C::TestCompile("getrandom", <<'FINIS' ) ? 1 : 0;
+#include <sys/random.h>
+int main(void)
+{
+       char c;
+       getrandom(&c, 1, 0);
+       return 0;
+}
+FINIS
+});
+
 # Check if we want to use monotonic clock
 TestBool("CONFIG_UCW_MONOTONIC_CLOCK", "Checking for monotonic clock", sub {
        return Get("CONFIG_LINUX");
index 9306b484d16e60f5e89239dc5e3b89a6afb2bd07..7ba31814b58a72f8a04bc51e01de6828cfebc725 100644 (file)
@@ -301,11 +301,15 @@ void fastrand_set_seed(struct fastrand *c, fastrand_seed_t seed)
 
 fastrand_seed_t fastrand_gen_seed_value(void)
 {
+  // Prefer kernel's urandom if getrandom() syscall is available.
+  u64 val;
+  if (strongrand_getrandom_mem_try(&val, sizeof(val)))
+    return val;
+
   // Generate a seed value as a mixture of:
   // -- current time
   // -- process id to deal with frequent fork() or startups
   // -- counter to deal with multiple contexts, for example in threaded application
-  u64 val;
   static u64 static_counter;
   ucwlib_lock();
   val = (static_counter += 0x1927a7639274162dLLU);
diff --git a/ucw/random-strong.c b/ucw/random-strong.c
new file mode 100644 (file)
index 0000000..3b19f5f
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ *     UCW Library -- Strong random generator
+ *
+ *     (c) 2020--2022 Pavel Charvat <pchar@ucw.cz>
+ *
+ *     This software may be freely distributed and used according to the terms
+ *     of the GNU Lesser General Public License.
+ */
+
+#undef LOCAL_DEBUG
+
+#include <ucw/lib.h>
+#include <ucw/random.h>
+#include <ucw/threads.h>
+
+#include <errno.h>
+#include <string.h>
+
+#ifdef CONFIG_UCW_GETRANDOM
+#include <sys/random.h>
+#endif
+
+/*** getrandom() helpers ***/
+
+#ifdef CONFIG_UCW_GETRANDOM
+static bool strongrand_getrandom_detect(void)
+{
+  static int static_detected;
+  int detected;
+  ucwlib_lock();
+  detected = static_detected;
+  if (detected)
+    {
+      ucwlib_unlock();
+      return detected > 0;
+    }
+  byte buf[1];
+  int err;
+  while (1)
+    {
+      int n = getrandom(buf, 1, GRND_NONBLOCK);
+      if (n >= 0)
+       {
+         ASSERT(n == 1);
+         detected = 1;
+         break;
+       }
+      else if (errno != EINTR)
+       {
+         err = errno;
+         detected = -1;
+         break;
+       }
+    }
+  static_detected = detected;
+  ucwlib_unlock();
+  if (detected > 0)
+    {
+      DBG("RANDOM: Kernel supports getrandom()");
+      return true;
+    }
+  else if (err == ENOSYS)
+    {
+      DBG("RANDOM: Kernel does not support getrandom()");
+      return false;
+    }
+  else
+    {
+      // We print an error also for EAGAIN -- for urandom it should be possible only during early boot.
+      msg(L_ERROR, "RANDOM: Failed to call getrandom(): %s -> assuming no support", strerror(err));
+      return false;
+    }
+}
+
+bool strongrand_getrandom_mem_try(void *buf, size_t size)
+{
+  if (!strongrand_getrandom_detect())
+    return false;
+  while (size)
+    {
+      ssize_t n = getrandom(buf, size, 0);
+      if (n < 0)
+       {
+         if (errno == EINTR)
+           continue;
+         die("RANDOM: Failed to call getrandom(): %m");
+       }
+      buf = (byte *)buf + n;
+      size -= n;
+    }
+  return true;
+}
+#else
+bool strongrand_getrandom_mem_try(void *buf UNUSED, size_t size UNUSED)
+{
+  return false;
+}
+#endif
index 0a287d189f7885486f17b6b5f27c869b13a54ee7..1d3b8a1450f5bd855321a317534ec1b8e4019218 100644 (file)
@@ -25,6 +25,7 @@
 #define fastrand_max_u64 ucw_fastrand_max_u64
 #define fastrand_mem ucw_fastrand_mem
 #define fastrand_double ucw_fastrand_double
+#define strongrand_getrandom_mem_try ucw_strongrand_getrandom_mem_try
 #endif
 
 /* random-fast.c */
@@ -55,7 +56,10 @@ void fastrand_set_seed(struct fastrand *c, fastrand_seed_t seed);
 /**
  * Generate a seed for fastrand_set_seed().
  *
- * We currently mix these things (you can add more if needed):
+ * Since glibc 2.25 and kernel 3.17, we use getrandom() syscall
+ * to read kernel's urandom.
+ *
+ * Otherwise we mix these things:
  * -- current time
  * -- process id to deal with frequent fork() or startups
  * -- counter to deal with multiple contexts, for example in threaded application
@@ -98,4 +102,11 @@ void fastrand_mem(struct fastrand *c, void *ptr, size_t size);
 /** Uniformly generate a random number from range [0.0, 1.0) + possible inaccuracies from basic floating point operations. **/
 double fastrand_double(struct fastrand *c);
 
+/* random-strong.c */
+
+/* FIXME: interface for strong randomness */
+
+// Internals
+bool strongrand_getrandom_mem_try(void *buf, size_t size);
+
 #endif