@@ -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
@@ -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;
}
new file mode 100644
@@ -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)
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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"
@@ -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
new file mode 100644
@@ -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)
@@ -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