]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/randutils: improve getrandom() usage
authorKarel Zak <kzak@redhat.com>
Mon, 14 Aug 2017 08:33:06 +0000 (10:33 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 14 Aug 2017 08:33:06 +0000 (10:33 +0200)
The getrandom() does not have to return all requested bytes (missing
entropy or when interrupted by signal). The current implementation in
util-linux stupidly asks for all random data again, rather than only
for missing bytes.

The current code also does not care if we repeat our requests for
ever; that's bad.

This patch uses the same way as we already use for reading from
/dev/urandom. It means:

 * repeat getrandom() for only missing bytes
 * limit number of unsuccessful request (16 times)
 * fallback to /dev/urandom on ENOSYS (old kernel or so...)

Addresses: https://github.com/karelzak/util-linux/issues/496
Signed-off-by: Karel Zak <kzak@redhat.com>
lib/randutils.c

index 7d85dc8417dc00730152303faca034e78a1656d1..5cd1c9976e2901b641a9140d497d536633e52e08 100644 (file)
@@ -95,27 +95,39 @@ int random_get_fd(void)
  */
 void random_get_bytes(void *buf, size_t nbytes)
 {
-       size_t i;
        unsigned char *cp = (unsigned char *)buf;
+       size_t i, n = nbytes;
+       int lose_counter = 0;
 
 #ifdef HAVE_GETRANDOM
-       errno = 0;
-       while (getrandom(buf, nbytes, 0) != (ssize_t)nbytes) {
-               if (errno == EINTR)
+       while (n > 0) {
+               int x;
+
+               errno = 0;
+               x = getrandom(cp, n, 0);
+               if (x > 0) {                    /* success */
+                      n -= x;
+                      cp += x;
+                      lose_counter = 0;
+               } else if (errno == ENOSYS) {   /* kernel without getrandom() */
+                       break;
+               } else {
+                       if (lose_counter++ > 16) /* entropy problem? */
+                               break;
                        continue;
-               break;
+               }
        }
+
        if (errno == ENOSYS)
+#endif
+
        /*
-        * We've been built against headers that support getrandom,
-        * but the running kernel does not.
-        * Fallback to reading from /dev/{u,}random as before
+        * We've been built against headers that support getrandom, but the
+        * running kernel does not.  Fallback to reading from /dev/{u,}random
+        * as before
         */
-#endif
        {
-               size_t n = nbytes;
                int fd = random_get_fd();
-               int lose_counter = 0;
 
                if (fd >= 0) {
                        while (n > 0) {
@@ -153,7 +165,6 @@ void random_get_bytes(void *buf, size_t nbytes)
                       sizeof(ul_jrand_seed)-sizeof(unsigned short));
        }
 #endif
-
        return;
 }
 
@@ -181,15 +192,32 @@ const char *random_tell_source(void)
 }
 
 #ifdef TEST_PROGRAM_RANDUTILS
-int main(int argc __attribute__ ((__unused__)),
-         char *argv[] __attribute__ ((__unused__)))
+int main(int argc, char *argv[])
 {
-       unsigned int v, i;
+       size_t i, n;
+       int64_t *vp, v;
+       char *buf;
+       size_t bufsz;
+
+       n = argc == 1 ? 16 : atoi(argv[1]);
 
-       /* generate and print 10 random numbers */
-       for (i = 0; i < 10; i++) {
+       printf("Multiple random calls:\n");
+       for (i = 0; i < n; i++) {
                random_get_bytes(&v, sizeof(v));
-               printf("%d\n", v);
+               printf("#%02zu: %25ju\n", i, v);
+       }
+
+
+       printf("One random call:\n");
+       bufsz = n * sizeof(*vp);
+       buf = malloc(bufsz);
+       if (!buf)
+               err(EXIT_FAILURE, "failed to allocate buffer");
+
+       random_get_bytes(buf, bufsz);
+       for (i = 0; i < n; i++) {
+               vp = (int64_t *) (buf + (i * sizeof(*vp)));
+               printf("#%02zu: %25ju\n", i, *vp);
        }
 
        return EXIT_SUCCESS;