From: Pavel Charvat Date: Tue, 22 Mar 2022 15:47:06 +0000 (+0000) Subject: Random: Detect and use getrandom() to seed fastrand. X-Git-Tag: v6.5.14~4 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=42632711a9eb7c3c732530e28388530da9e7f1cc;p=libucw.git Random: Detect and use getrandom() to seed fastrand. --- diff --git a/ucw/Makefile b/ucw/Makefile index 6fdb641f..87f328c3 100644 --- a/ucw/Makefile +++ b/ucw/Makefile @@ -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 \ diff --git a/ucw/default.cfg b/ucw/default.cfg index 3d4b4881..a0441b0f 100644 --- a/ucw/default.cfg +++ b/ucw/default.cfg @@ -1,5 +1,6 @@ # Configuration variables of the UCW library and their default values # (c) 2005--2015 Martin Mares +# (c) 2020 Pavel Charvat # 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"); diff --git a/ucw/perl/UCW/Configure/LibUCW.pm b/ucw/perl/UCW/Configure/LibUCW.pm index 7feef1e2..4bf8adbd 100644 --- a/ucw/perl/UCW/Configure/LibUCW.pm +++ b/ucw/perl/UCW/Configure/LibUCW.pm @@ -2,6 +2,7 @@ # (c) 2005--2012 Martin Mares # (c) 2006 Robert Spalek # (c) 2008 Michal Vaner +# (c) 2020 Pavel Charvat 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 +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"); diff --git a/ucw/random-fast.c b/ucw/random-fast.c index 9306b484..7ba31814 100644 --- a/ucw/random-fast.c +++ b/ucw/random-fast.c @@ -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 index 00000000..3b19f5fd --- /dev/null +++ b/ucw/random-strong.c @@ -0,0 +1,98 @@ +/* + * UCW Library -- Strong random generator + * + * (c) 2020--2022 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#undef LOCAL_DEBUG + +#include +#include +#include + +#include +#include + +#ifdef CONFIG_UCW_GETRANDOM +#include +#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 diff --git a/ucw/random.h b/ucw/random.h index 0a287d18..1d3b8a14 100644 --- a/ucw/random.h +++ b/ucw/random.h @@ -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