Ver código fonte

Handle linux getrandom returning EINTR on interrupts/signals

Also handle ENOTSUP failures where aria2 was build with linux headers
newer than the actual running kernel.

Fixes GH-336
Nils Maier 10 anos atrás
pai
commit
ba0e32abae
3 arquivos alterados com 49 adições e 7 exclusões
  1. 19 3
      src/SimpleRandomizer.cc
  2. 0 2
      src/SimpleRandomizer.h
  3. 30 2
      src/getrandom_linux.c

+ 19 - 3
src/SimpleRandomizer.cc

@@ -42,6 +42,8 @@
 
 #include "a2time.h"
 #include "a2functional.h"
+#include "LogFactory.h"
+#include "fmt.h"
 
 #ifdef HAVE_GETRANDOM_INTERFACE
 #  include "getrandom_linux.h"
@@ -88,10 +90,24 @@ void SimpleRandomizer::getRandomBytes(unsigned char* buf, size_t len)
 #ifdef __MINGW32__
   BOOL r = CryptGenRandom(provider_, len, reinterpret_cast<BYTE*>(buf));
   assert(r);
-#elif defined(HAVE_GETRANDOM_INTERFACE)
-  auto rv = getrandom_linux(buf, len);
-  assert(rv >= 0 && (size_t)rv == len);
 #else // ! __MINGW32__
+#if defined(HAVE_GETRANDOM_INTERFACE)
+  static bool have_random_support = true;
+  if (have_random_support) {
+    auto rv = getrandom_linux(buf, len);
+    if (rv != -1 || errno != ENOSYS) {
+      if (rv < -1) {
+        A2_LOG_ERROR(fmt("Failed to produce randomness: %d", errno));
+      }
+      assert(rv >= 0 && (size_t)rv == len);
+      return;
+    }
+    have_random_support = false;
+    A2_LOG_INFO("Disabled getrandom support, because kernel does not "\
+        "implement this feature (ENOSYS)");
+  }
+  // Fall through to generic implementation
+#endif // defined(HAVE_GETRANDOM_INTERFACE)
   auto ubuf = reinterpret_cast<result_type*>(buf);
   size_t q = len / sizeof(result_type);
   auto gen = std::uniform_int_distribution<result_type>();

+ 0 - 2
src/SimpleRandomizer.h

@@ -54,8 +54,6 @@ private:
 private:
 #ifdef __MINGW32__
   HCRYPTPROV provider_;
-#elif defined(HAVE_GETRANDOM_INTERFACE)
-  // Nothing special needed
 #else
   std::random_device dev_;
 #endif // ! __MINGW32__

+ 30 - 2
src/getrandom_linux.c

@@ -37,14 +37,42 @@
 #include <unistd.h>
 #include <sys/syscall.h>
 #include <linux/random.h>
+#include <errno.h>
+#include <linux/errno.h>
+#include <stdint.h>
+#include <stdio.h>
 
 #include "config.h"
 #include "getrandom_linux.h"
 
 int getrandom_linux(void *buf, size_t buflen) {
+  int rv = 0;
+  uint8_t* p = buf;
+  while (buflen) {
+    int read;
 #ifdef HAVE_GETRANDOM
-  return getrandom(buf, buflen, 0);
+    read = getrandom(p, buflen, 0);
 #else // HAVE_GETRANDOM
-  return syscall(SYS_getrandom, buf, buflen, 0);
+    read = syscall(SYS_getrandom, p, buflen, 0);
+    /* Some libc impl. might mess this up */
+    if (read == -EINTR || read == -ERESTART) {
+      errno = EINTR;
+      read = -1;
+    }
+    if (read < -1) {
+      errno = -read;
+      read = -1;
+    }
 #endif // HAVE_GETRANDOM
+    if (read < 0) {
+      if (errno == EINTR) {
+        continue;
+      }
+      return -1;
+    }
+    p += read;
+    rv += read;
+    buflen -= read;
+  }
+  return rv;
 }