This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc 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]

[PATCH] Add getrandom implementation [BZ #17252]


The emulation opens /dev/random and /dev/urandom and uses
these descriptors.  There are safeguards to detect application
which have overridden these descriptors.

The getrandom function is defined as a macro in such a way that
a direct attempt to define a getrandom function will either
fail to compile, or fail to interpose the __getrandom symbol.
The intent is that legacy implementations will not accidentally
preempt the implementation in libc (which could well be used
by other libraries in the same process image).

2016-06-10  Florian Weimer  <fweimer@redhat.com>

	[BZ #17252]
	* posix/Makefile (routines): Add getrandom.
	(tests): Add tst-getrandom.
	* posix/getrandom.c: New file.
	* posix/getrandom_emulation.c: Likewise.
	* posix/tst-getrandom.c: Likewise.
	* posix/Versions (GLIBC_2.24.getrandom): Add getrandom,
	__getrandom.
	* posix/unistd.h (__getrandom): Declare.
	(getrandom): Define.
	* sysdeps/unix/sysv/linux/kernel-features.h
	(__ASSUME_GETRANDOM_SYSCALL): Define.

diff --git a/posix/Makefile b/posix/Makefile
index 5b0e298..68ad2cc 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -59,7 +59,8 @@ routines :=								      \
 	spawnattr_getsigmask spawnattr_getschedpolicy spawnattr_getschedparam \
 	spawnattr_setsigmask spawnattr_setschedpolicy spawnattr_setschedparam \
 	posix_madvise							      \
-	get_child_max sched_cpucount sched_cpualloc sched_cpufree
+	get_child_max sched_cpucount sched_cpualloc sched_cpufree \
+	getrandom
 
 aux		:= init-posix environ
 tests		:= tstgetopt testfnm runtests runptests	     \
@@ -90,7 +91,7 @@ tests		:= tstgetopt testfnm runtests runptests	     \
 		   bug-getopt5 tst-getopt_long1 bug-regex34 bug-regex35 \
 		   tst-pathconf tst-getaddrinfo4 tst-rxspencer-no-utf8 \
 		   tst-fnmatch3 bug-regex36 tst-getaddrinfo5 \
-		   tst-posix_spawn-fd
+		   tst-posix_spawn-fd tst-getrandom
 xtests		:= bug-ga2
 ifeq (yes,$(build-shared))
 test-srcs	:= globtest
diff --git a/posix/Versions b/posix/Versions
index bb481a5..23f2c85 100644
--- a/posix/Versions
+++ b/posix/Versions
@@ -134,6 +134,20 @@ libc {
   GLIBC_2.11 {
     execvpe;
   }
+
+  # We use a dedicated symbol version so that we can scan the
+  # .gnu.version_r section to identify binaries which use the
+  # getrandom function.  This allows us to avoid lazy opening of the
+  # emulation file descriptors if this proves too error-prone.
+  #
+  # The getrandom alias is for configure checks; applications
+  # will call __getrandom instead (through a macro wrapper,
+  # which is intended to prevent accidental interposition).
+  GLIBC_2.24.getrandom {
+    getrandom;
+    __getrandom;
+  }
+
   GLIBC_PRIVATE {
     __libc_fork; __libc_pread; __libc_pwrite;
   }
diff --git a/posix/getrandom.c b/posix/getrandom.c
new file mode 100644
index 0000000..57d18ac
--- /dev/null
+++ b/posix/getrandom.c
@@ -0,0 +1,32 @@
+/* Generic version of getrandom, based on emulation.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "getrandom_emulation.c"
+
+ssize_t
+__getrandom (void *buffer, size_t length, unsigned flags)
+{
+  return getrandom_emulation (buffer, length, flags);
+}
+
+/* We use a macro wrapper to make accidental interposition of an
+   incompatible version less likely.  The getrandom alias is provided
+   so that configure checks which do not use a proper prototype will
+   work.  */
+#undef getrandom
+strong_alias (__getrandom, getrandom)
diff --git a/posix/getrandom_emulation.c b/posix/getrandom_emulation.c
new file mode 100644
index 0000000..89886b1
--- /dev/null
+++ b/posix/getrandom_emulation.c
@@ -0,0 +1,264 @@
+/* Emulation of the getrandom system call.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <atomic.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <not-cancel.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#if __WORDSIZE == 32
+
+/* On 32-bit architectures, we need to split the value so that we can
+   use atomics.  */
+typedef struct
+{
+  uint32_t low;
+  uint32_t high;
+} getrandom_fd_attribute;
+
+static void
+getrandom_fd_attribute_store (getrandom_fd_attribute *target,
+                              unsigned long long source)
+{
+  atomic_store_relaxed (&target->low, (uint32_t) source);
+  atomic_store_relaxed (&target->high, (uint32_t) (source >> 32));
+}
+
+static bool
+getrandom_fd_attribute_equal (getrandom_fd_attribute *actual,
+                              unsigned long long expected)
+{
+  uint32_t expected_low = expected;
+  uint32_t expected_high = expected >> 32;
+  return atomic_load_relaxed (&actual->low) == expected_low
+    && atomic_load_relaxed (&actual->high) == expected_high;
+}
+
+#else  /* __WORDSIZE != 32 */
+
+_Static_assert (__WORDSIZE == 64, "supported word size");
+
+/* On 64-bit architectures, we can use atomics directly.  */
+typedef uint64_t getrandom_fd_attribute;
+
+static void
+getrandom_fd_attribute_store (getrandom_fd_attribute *target,
+                              getrandom_fd_attribute source)
+{
+  atomic_store_relaxed (target, source);
+}
+
+static bool
+getrandom_fd_attribute_equal (getrandom_fd_attribute *actual,
+                              getrandom_fd_attribute expected)
+{
+  return atomic_load_relaxed (actual) == expected;
+}
+
+#endif  /* __WORDSIZE */
+
+/* The size of dev_t varies across architectures.  */
+typedef __typeof__ (((struct stat64) {}).st_dev) getrandom_dev_t;
+
+_Static_assert (sizeof (getrandom_dev_t) <= sizeof (getrandom_fd_attribute),
+                "size of dev_t");
+_Static_assert (sizeof (ino64_t) <= sizeof (getrandom_fd_attribute),
+                "size of ino64_t");
+
+/* We pair each emulation file descriptor with the device and inode
+   value at the time of the open call.  This will allow us to detect
+   if an application has messed with the file descriptor.  */
+struct getrandom_emulation_fd
+{
+  int fd;
+  getrandom_fd_attribute dev;
+  getrandom_fd_attribute ino;
+};
+
+static void
+__attribute__ ((noreturn))
+getrandom_fd_failure (const char *device, const char *op, int fd)
+{
+  char descriptor[32];
+  if (fd >= 0)
+    __snprintf (descriptor, sizeof (descriptor), ", descriptor %d", fd);
+  else
+    descriptor[0] = '\0';
+  char message[128];
+  __snprintf (message, sizeof (message),
+              "Could not %s randomness source %s, error code %d%s",
+              op, device, errno, descriptor);
+  __libc_fatal (message);
+}
+
+static int
+getrandom_validate_fd
+  (const char *device, int fd, struct getrandom_emulation_fd *pfd)
+{
+  struct stat64 st;
+  if (fstat64 (fd, &st) < 0)
+    getrandom_fd_failure (device, "fstat64", fd);
+  if (getrandom_fd_attribute_equal (&pfd->dev, st.st_dev)
+      && getrandom_fd_attribute_equal (&pfd->ino, st.st_ino))
+    return fd;
+  char message[256];
+  __snprintf (message, sizeof (message),
+              "Unexpected inode for randomness source %s"
+              " (descriptor %d): %llu/%llu",
+              device, fd, (unsigned long long) st.st_dev,
+              (unsigned long long) st.st_ino);
+  __libc_fatal (message);
+}
+
+/* Try to relocate the descriptor FD to a descriptor >= TARGET.
+   Returns the new descriptor on success, or the old descriptor on
+   failure.  */
+static int
+getrandom_move_fd_target (int fd, int target)
+{
+  int newfd = fcntl_not_cancel (fd, F_DUPFD, target);
+  if (newfd >= 0)
+    close_not_cancel (fd);
+  else
+    newfd = fd;
+  return newfd;
+}
+
+/* Try to move the file descriptor FD out of the way, to make it less
+   likely that it will interfere with application use (such as the
+   select function).  Returns the file descriptor (which may have
+   changed).  */
+static int
+getrandom_move_fd (int fd)
+{
+  int newfd = getrandom_move_fd_target (fd, 1024);
+  if (newfd == fd)
+    newfd = getrandom_move_fd_target (fd, 512);
+  return newfd;
+}
+
+/* If *PFD->fd is negative, atomically replace it with a file
+   descriptor for DEVICE.  If NONBLOCK is true, make the descriptor
+   non-blocking.  Return the file descriptor.  */
+static int
+getrandom_get_fd (const char *device, struct getrandom_emulation_fd *pfd,
+                  bool nonblock)
+{
+  while (true)
+    {
+      /* Synchronize with the CAS below.  */
+      int fd = atomic_load_acquire (&pfd->fd);
+      if (fd >= 0)
+        return getrandom_validate_fd (device, fd, pfd);
+
+      /* We need to obtain a file descriptor for the random
+         device.  */
+      {
+        int flags = O_RDONLY;
+        if (nonblock)
+          flags |= O_NONBLOCK;
+        fd = open_not_cancel (device, flags, 0);
+      }
+      if (fd < 0)
+        getrandom_fd_failure (device, "open", -1);
+      fd = getrandom_move_fd (fd);
+
+      /* Obtain inode information to validate the file descriptor
+         later.  */
+      struct stat64 st;
+      if (fstat64 (fd, &st) < 0)
+        getrandom_fd_failure (device, "fstat64", fd);
+
+      /* We always write the same value, so it does not matter which
+         value is read by getrandom_validate_fd.  */
+      getrandom_fd_attribute_store (&pfd->dev, st.st_dev);
+      getrandom_fd_attribute_store (&pfd->ino, st.st_ino);
+
+      int expected = -1;
+      /* Synchronize with the atomic_load_acquire above.  */
+      if (atomic_compare_exchange_weak_release (&pfd->fd, &expected, fd))
+        return fd;
+
+      /* The CAS failed.  Close the file descriptor and try again.  */
+      close_not_cancel (fd);
+    }
+}
+
+/* The file descriptors used for emulation.  */
+static struct getrandom_emulation_fd getrandom_random_fd = { .fd = -1 };
+static struct getrandom_emulation_fd getrandom_nonblock_fd = { .fd = -1 };
+static struct getrandom_emulation_fd getrandom_urandom_fd = { .fd = -1 };
+
+#define GETRANDOM_SUPPORTED_FLAGS (GRND_RANDOM | GRND_NONBLOCK)
+
+static ssize_t
+getrandom_emulation (void *buffer, size_t length, unsigned flags)
+{
+  /* Check if any unsupported flags have been requested.  */
+  if (flags & ~GETRANDOM_SUPPORTED_FLAGS)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  if (flags & GRND_RANDOM)
+    {
+      int fd;
+      const char *device = "/dev/random";
+      if (flags & GRND_NONBLOCK)
+        fd = getrandom_get_fd (device, &getrandom_nonblock_fd, true);
+      else
+        fd = getrandom_get_fd (device, &getrandom_random_fd, false);
+      ssize_t ret = __read (fd, buffer, length);
+      if (ret < 0 && errno == EBADF)
+        /* EBADF errors are fatal.  */
+        getrandom_fd_failure (device, "read", fd);
+      /* The caller is expected to handle any error.  */
+      return ret;
+    }
+
+  /* GRND_RANDOM is not set.  We need to terminate the process on
+     failure, to mirror the system call behavior (the system call is
+     guaranteed not to fail after initialization).  */
+
+  const char *device = "/dev/urandom";
+
+  /* /dev/urandom is not supposed to block, so we ignore the
+     GRND_NONBLOCK flag.  */
+  int fd = getrandom_get_fd (device, &getrandom_urandom_fd, false);
+
+  void *end = buffer + length;
+  while (buffer < end)
+    {
+      ssize_t ret = TEMP_FAILURE_RETRY
+        (read_not_cancel (fd, buffer, end - buffer));
+      if (ret < 0)
+        getrandom_fd_failure (device, "read", fd);
+      buffer += ret;
+
+      /* Paranoia: This catches cases where the application exchanges
+         another file descriptor which return EOF.  We would have an
+         infinite loop otherwise.  */
+      getrandom_validate_fd (device, fd, &getrandom_urandom_fd);
+    }
+
+  return length;
+}
diff --git a/posix/tst-getrandom.c b/posix/tst-getrandom.c
new file mode 100644
index 0000000..11a811c
--- /dev/null
+++ b/posix/tst-getrandom.c
@@ -0,0 +1,162 @@
+/* Tests for the getrandom function.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Set to true if any errors is encountered.  */
+static bool errors;
+
+/* Test getrandom with a single buffer length.  */
+static void
+test_length (char *buffer, int length, unsigned flags)
+{
+  memset (buffer, 0, length);
+  strcpy (buffer + length, "123");
+  ssize_t ret = getrandom (buffer, length, flags);
+  if (ret < 0)
+    {
+      if (!((flags & GRND_RANDOM)
+            && (flags & GRND_NONBLOCK)
+            && errno != EAGAIN))
+        {
+          printf ("error: getrandom (%d, 0x%x): %m\n", length, flags);
+          errors = true;
+        }
+    }
+  if (ret != length)
+    {
+      if (flags & GRND_RANDOM)
+        {
+          if (ret == 0 || ret > length)
+            {
+              printf ("error: getrandom (%d, 0x%x) returned %zd\n",
+                      length, flags, ret);
+              errors = true;
+            }
+        }
+      else
+        {
+          printf ("error: getrandom (%d, 0x%x) returned %zd\n",
+                  length, flags, ret);
+          errors = true;
+        }
+    }
+  if (length >= 7)
+    {
+      /* One spurious test failure in 2**56 is sufficiently
+         unlikely.  */
+      int non_null = 0;
+      for (int i = 0; i < length; ++i)
+        non_null += buffer[i] != 0;
+      if (non_null == 0)
+        {
+          printf ("error: getrandom (%d, 0x%x) returned all-zero bytes\n",
+                  length, flags);
+          errors = true;
+        }
+    }
+  if (memcmp (buffer + length, "123", 4) != 0)
+    {
+      printf ("error: getrandom (%d, 0x%x) wrote spurios bytes\n",
+              length, flags);
+      errors = true;
+    }
+}
+
+/* Call getrandom repeatedly to fille the buffer.  */
+static bool
+getrandom_full (char *buffer, int length, unsigned flags)
+{
+  char *end = buffer + length;
+  while (buffer < end)
+    {
+      ssize_t ret = getrandom (buffer, end - buffer, flags);
+      if (ret < 0)
+        {
+          printf ("error: getrandom (%d, 0x%x): %m\n", length, flags);
+          errors = true;
+          return false;
+        }
+      buffer += ret;
+    }
+
+  return true;
+}
+
+static void
+test_flags (unsigned flags)
+{
+  /* Test various lengths, but only for !GRND_RANDOM, to conserve
+     entropy.  */
+  {
+    enum { max_length = 300 };
+    char buffer[max_length + 4];
+    if (flags & GRND_RANDOM)
+      test_length (buffer, 0, flags);
+    else
+      {
+        for (int length = 0; length <= 9; ++length)
+          test_length (buffer, length, flags);
+        test_length (buffer, 16, flags);
+        test_length (buffer, max_length, flags);
+      }
+  }
+
+  /* Test that getrandom returns different data.  */
+  if (!(flags & GRND_NONBLOCK))
+    {
+      char buffer1[8];
+      memset (buffer1, 0, sizeof (buffer1));
+
+      char buffer2[8];
+      memset (buffer2, 0, sizeof (buffer2));
+
+      if (getrandom_full (buffer1, sizeof (buffer1), flags)
+          && getrandom_full (buffer1, sizeof (buffer1), flags))
+        {
+          if (memcmp (buffer1, buffer2, sizeof (buffer1)) == 0)
+            {
+              printf ("error: getrandom returns constant value\n");
+              errors = true;
+            }
+        }
+    }
+}
+
+static int
+do_test (void)
+{
+  for (int use_random = 0; use_random < 2; ++use_random)
+    for (int use_nonblock = 0; use_nonblock < 2; ++use_nonblock)
+      {
+        int flags = 0;
+        if (use_random)
+          flags |= GRND_RANDOM;
+        if (use_nonblock)
+          flags |= GRND_NONBLOCK;
+        test_flags (flags);
+      }
+  return errors;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/posix/unistd.h b/posix/unistd.h
index 625ba77..1360310 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -1157,6 +1157,16 @@ extern int pthread_atfork (void (*__prepare) (void),
 			   void (*__child) (void)) __THROW;
 #endif
 
+#ifdef __USE_GNU
+/* Flags for use with  getrandom.  */
+# define GRND_NONBLOCK 1
+# define GRND_RANDOM 2
+
+ssize_t __getrandom (void *__buffer, size_t __length, unsigned __flags);
+#define getrandom(buffer, length, flags) \
+  (0 + __getrandom (buffer, length, flags))
+
+#endif	/* __USE_GNU */
 
 /* Define some macros helping to catch buffer overflows.  */
 #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
diff --git a/sysdeps/unix/sysv/linux/getrandom.c b/sysdeps/unix/sysv/linux/getrandom.c
new file mode 100644
index 0000000..3bf6f85
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/getrandom.c
@@ -0,0 +1,149 @@
+/* Linux version of getrandom.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <kernel-features.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#undef getrandom
+
+#ifdef __NR_getrandom
+
+/* Wrapper which can only process INT_MAX bytes at a time.  */
+static int
+getrandom_syscall_intmax (void *buffer, size_t length, unsigned flags)
+{
+  int ret;
+  do
+    ret = INLINE_SYSCALL (getrandom, 3, buffer, length, flags);
+  /* getrandom can fail with EINTR in the blocking urandom case if the
+     pool has not been initialized yet.  Retry in this case.  */
+  while (ret < 0
+         && !(flags & GRND_RANDOM)
+         && !(flags & GRND_NONBLOCK)
+         && errno == EINTR);
+  return ret;
+}
+
+/* System call wrapper which can process all lengths and retries on
+   EINTR if necessary.  */
+static ssize_t
+getrandom_syscall (void *buffer, size_t length, unsigned flags)
+{
+  void *p = buffer;
+  void *end = buffer + length;
+
+  /* Execute the system call even for length == 0, so that we properly
+     reported ENOSYS.  Otherwise, the detection of system call
+     availability will not work.  */
+  do
+    {
+      size_t to_get = end - p;
+      /* The system call returns an int, so it cannot process more
+         than INT_MAX bytes at a time.  */
+      if (to_get > INT_MAX)
+        to_get = INT_MAX;
+      int ret = getrandom_syscall_intmax (p, to_get, flags);
+      /* Stop on error. */
+      if (ret < 0)
+        return ret;
+      p += ret;
+      /* Stop on a short read, unless no flags were specified.  In
+         this case, we continue calling the system call, so that
+         application code does not have to deal with short reads.  */
+      if (ret < to_get && flags != 0)
+        return p - buffer;
+    }
+  while (p < end);
+
+  return length;
+}
+
+
+# ifdef __ASSUME_GETRANDOM_SYSCALL
+ssize_t
+__getrandom (void *buffer, size_t length, unsigned flags)
+{
+  ssize_t ret = getrandom_syscall (buffer, length, flags);
+  if (ret < 0 && ret == ENOSYS)
+    __libc_fatal ("getrandom system call failed with ENONSYS");
+  return ret;
+}
+
+# else /* !__ASSUME_GETRANDOM_SYSCALL */
+#  include "getrandom_emulation.c"
+
+/* Possible values: 0: not initialized, 1: system call present,
+   2: system call missing.  */
+static int have_getrandom;
+
+ssize_t
+__getrandom (void *buffer, size_t length, unsigned flags)
+{
+  /* Relaxed MO means that we may issue some additional failing system
+     calls because concurrent calls to __getrandom are not
+     synchronized, but optimizing for repeated calls is more
+     important.  */
+  switch (atomic_load_relaxed (&have_getrandom))
+    {
+    case 0:
+      /* Not yet initialized.  */
+      {
+        ssize_t ret = getrandom_syscall (buffer, length, flags);
+        if (ret < 0 && errno == ENOSYS)
+          {
+            /* Record that the system call is missign and fall back to
+               emulation.  */
+            atomic_store_relaxed (&have_getrandom, 2);
+            return getrandom_emulation (buffer, length, flags);
+          }
+        atomic_store_relaxed (&have_getrandom, 1);
+        return ret;
+      }
+    case 1:
+      /* System call is available.  */
+      return getrandom_syscall (buffer, length, flags);
+    case 2:
+      /* System call is missing.  */
+      return getrandom_emulation (buffer, length, flags);
+    }
+  abort ();
+}
+# endif /* __ASSUME_GETRANDOM_SYSCALL */
+
+#else  /* !__NR_getrandom */
+
+/* The kernel headers do not mention the getrandom system call.  We
+   can only perform emulation. */
+
+# include "getrandom_emulation.c"
+
+ssize_t
+__getrandom (void *buffer, size_t length, unsigned flags)
+{
+  return getrandom_emulation (buffer, length, flags);
+}
+
+#endif
+
+/* We use a macro wrapper to make accidental interposition of an
+   incompatible version less likely.  The getrandom alias is provided
+   so that configure checks which do not use a proper prototype will
+   work.  */
+strong_alias (__getrandom, getrandom)
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index 02c530b..eb41124 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -151,3 +151,8 @@
    separate syscalls were only added later.  */
 #define __ASSUME_SENDMSG_SYSCALL	1
 #define __ASSUME_RECVMSG_SYSCALL	1
+
+/* getrandom was added on many architectures in Linux 3.17. */
+#if __LINUX_KERNEL_VERSION >= 0x031100
+# define __ASSUME_GETRANDOM_SYSCALL
+#endif


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