[PATCH v2] Add getrandom implementation [BZ #17252]
Rical Jasan
ricaljasan@pacific.net
Thu Sep 8 11:58:00 GMT 2016
On 09/08/2016 02:53 AM, Florian Weimer wrote:
> On 06/30/2016 11:32 AM, Rical Jasan wrote:
>>> > +#ifndef _SYS_RANDOM_H
>>> > +#define _SYS_RANDOM_H
>> Isn't there a preferred way of doing this that defines _SYS_RANDOM_H to
>> 1? I seem to remember a wiki page that talked about protecting against
>> typos.
>
> This might defeat the header guard optimization. Existing practice
> varies, some headers use 1, some don't.
>
>> How strict is the coding style about unnecessary parentheses?
>
> I prefer adding braces to if statements if they are nested and some have
> else branches.
I won't argue with that. Looks clean that way.
>
>> Really though, reading the code, it made sense to me.
>
> Thanks. I'm attaching a version with your comments (not rebased to 2.25
> though). We may need this code again if we ever implement a
> arc4random-style interface.
>
> (I think the consensus for getrandom is to add only a thin wrapper
> around the system call, if we are going to add it at all.)
>
> Florian
I'll chip in a few more cents as well.
>
> getrandom-with-fallback.patch
>
>
> Add getrandom implementation [BZ #17252]
>
> The emulation opens /dev/random and /dev/urandom (depending
> on the flags), reads random bytes, and closes the descriptor
> again.
>
> 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 gzlibc (which could well be used
glibc?
> by other libraries in the same process image).
>
> 2016-06-27 Florian Weimer <fweimer@redhat.com>
>
> [BZ #17252]
> * stdlib/sys/random.h: New file.
> (headers): Add it.
> * stdlib/Makefile (routines): Add getrandom.
> (tests): Add tst-getrandom.
> * stdlib/Versions (GLIBC_2.24): Add __getrandom.
> * stdlib/getrandom.c: New file.
> * stdlib/tst-getrandom.c: Likewise.
> * sysdep/posix/getrandom.c: Likewise.
> * sysdep/posix/getrandom_emulation.c: Likewise.
> * sysdeps/unix/sysv/linux/getrandom.c: Likewise.
> * sysdeps/unix/sysv/linux/kernel-features.h
> (__ASSUME_GETRANDOM_SYSCALL): Define.
> * manual/crypt.texi (Unpredictable Bytes): New section.
> * manual/math.texi (Pseudo-Random Numbers): Add cross-reference.
> * sysdeps/arm/nacl/libc.abilist: Add __getrandom.
> * sysdeps/unix/sysv/linux/aarch64/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/alpha/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/arm/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/hppa/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/i386/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/ia64/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/microblaze/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/nios2/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist:
> Likewise.
> * sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist:
> Likewise.
> * sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist: Likewise.
> * sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/sh/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/x86_64/64/libc.abilist: Likewise.
> * sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist: Likewise.
>
> diff --git a/NEWS b/NEWS
> index e2737d5..c6b32de 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -14,6 +14,10 @@ Version 2.24
> unchanged). Linux 3.2 or later kernel headers are required on all
> architectures.
>
> +* The getrandom function and the <sys/random.h> header file have been added.
> + This function will use the Linux getrandom system call to obtain random
> + data if available.
> +
> * The pap_AN locale has been deleted. This has been deprecated for a long
> time. It has been replaced by pap_AW & pap_CW, both of which have long
> been included in previous releases.
> diff --git a/manual/crypt.texi b/manual/crypt.texi
> index 659688b..082ad70 100644
> --- a/manual/crypt.texi
> +++ b/manual/crypt.texi
> @@ -45,6 +45,7 @@ encrypted authentication use normal DES.
> * getpass:: Prompting the user for a password.
> * crypt:: A one-way function for passwords.
> * DES Encryption:: Routines for DES encryption.
> +* Unpredictable Bytes:: Randomness for cryptography purposes.
> @end menu
>
> @node Legal Problems
> @@ -428,3 +429,83 @@ each byte.
> The @code{ecb_crypt}, @code{cbc_crypt}, and @code{des_setparity}
> functions and their accompanying macros are all defined in the header
> @file{rpc/des_crypt.h}.
> +
> +@node Unpredictable Bytes
> +@section Generating Unpredictable Bytes
> +
> +Some cryptographic applications (such as session key generation) need
> +unpredictable bytes.
> +
> +@comment sys/random.h
> +@comment GNU
> +@deftypefun ssize_t getrandom (void *@var{buffer}, size_t @var{length}, unsigned int @var{flags})
> +@safety{@mtsafe{}@assafe{}@acsafe{}}
> +
> +This function writes @var{length} bytes of random data to the array
> +starting at @var{buffer}. On succes, this function returns the number
success
> +of bytes which have been written to the buffer (which can be less than
> +@var{length}). On error, @code{-1} is returned, and @code{errno} is
> +updated accordingly.
> +
> +The @code{getrandom} function is declared in the header file
> +@file{sys/random.h}. It is a GNU extension.
> +
> +The following flags are defined for the @var{flags} argument:
> +
> +@table @code
> +@item GRND_RANDOM
> +Use the blocking pool instead of the non-blocking pool to obtain
> +randomness. By default, the non-blocking pool is used. The blocking
> +pool corresponds to @file{/dev/random}, and the non-blocking pool to
> +@file{/dev/urandom}.
> +
> +@item GRND_NONBLOCK
> +Instead of blocking, return to the caller immediately if no data is
> +available.
> +@end table
> +
> +Even access to the non-blocking pool can block if the system has just
> +booted and the pool has not yet been initialized.
> +
> +If the @var{flags} argument is zero, the @code{getrandom} implementation
> +in @theglibc{} will only return once @var{length} bytes have been
> +written to @var{buffer}, or there is an error (except @code{EINTR}), and
> +such a function call is not a cancellation point.
> +
> +@strong{Note:} If the system lacks support for the @code{getrandom}
> +system call, the @code{getrandom} function uses emulation based on the
> +@file{/dev/random} and @file{/dev/urandom} device nodes. This results
> +in additional failure scenarios, listed below as ``emulation only''.
> +
> +The @code{getrandom} function can fail with several errors, some of
> +which are listed below. In addition, if @var{flags} is not zero, the
> +function may not fill the buffer completely and return a value less than
> +@var{length}.
> +
> +@table @code
> +@item EAGAIN
> +No random data was available and @code{GRND_NONBLOCK} was specified in
> +@var{flags}.
> +
> +@item EFAULT
> +The the combination of @var{buffer} and @var{length} arguments specifies
The the
> +an invalid memory range.
> +
> +@item EINTR
> +The system call was interrupted (only if flags is not zero).
@var{flags}
> +
> +@item EINVAL
> +The @var{flags} argument contains an invalid combination of flags.
> +
> +@item ENOENT
> +@itemx EACCES
> +The current file system namespace lacks the required device node, or the
> +device node is inaccessible (emulation only).
> +
> +@item EMFILE
> +@itemx ENFILE
> +The random device node could not be opened due to process or system
> +limits (emulation only).
> +@end table
> +
> +@end deftypefun
> diff --git a/manual/math.texi b/manual/math.texi
> index 5c9f7b9..917d598 100644
> --- a/manual/math.texi
> +++ b/manual/math.texi
> @@ -1413,7 +1413,8 @@ is convenient when you are debugging a program, but it is unhelpful if
> you want the program to behave unpredictably. If you want a different
> pseudo-random series each time your program runs, you must specify a
> different seed each time. For ordinary purposes, basing the seed on the
> -current time works well.
> +current time works well. For random numbers in cryptography,
> +@pxref{Unpredictable Bytes}.
>
> You can obtain repeatable sequences of numbers on a particular machine type
> by specifying the same initial seed value for the random number
I like the manual entries.
> diff --git a/stdlib/Makefile b/stdlib/Makefile
> index fc6f23d..9055993 100644
> --- a/stdlib/Makefile
> +++ b/stdlib/Makefile
> @@ -28,7 +28,7 @@ headers := stdlib.h bits/stdlib.h bits/stdlib-ldbl.h bits/stdlib-float.h \
> errno.h sys/errno.h bits/errno.h \
> ucontext.h sys/ucontext.h \
> alloca.h fmtmsg.h \
> - bits/stdlib-bsearch.h
> + bits/stdlib-bsearch.h sys/random.h
>
> routines := \
> atof atoi atol atoll \
> @@ -45,7 +45,7 @@ routines := \
> srand48 seed48 lcong48 \
> drand48_r erand48_r lrand48_r nrand48_r mrand48_r jrand48_r \
> srand48_r seed48_r lcong48_r \
> - drand48-iter \
> + drand48-iter getrandom \
> strtol strtoul strtoll strtoull \
> strtol_l strtoul_l strtoll_l strtoull_l \
> strtof strtod strtold \
> @@ -77,7 +77,7 @@ tests := tst-strtol tst-strtod testmb testrand testsort testdiv \
> tst-tininess tst-strtod-underflow tst-tls-atexit \
> tst-setcontext3 tst-tls-atexit-nodelete \
> tst-strtol-locale tst-strtod-nan-locale tst-strfmon_l \
> - tst-quick_exit tst-thread-quick_exit
> + tst-quick_exit tst-thread-quick_exit tst-getrandom
> tests-static := tst-secure-getenv
> ifeq ($(have-cxx-thread_local),yes)
> CFLAGS-tst-quick_exit.o = -std=c++11
> diff --git a/stdlib/Versions b/stdlib/Versions
> index 9c06b43..8d79f46 100644
> --- a/stdlib/Versions
> +++ b/stdlib/Versions
> @@ -111,6 +111,7 @@ libc {
> }
> GLIBC_2.24 {
> quick_exit;
> + __getrandom;
> }
> GLIBC_PRIVATE {
> # functions which have an additional interface since they are
> diff --git a/stdlib/getrandom.c b/stdlib/getrandom.c
> new file mode 100644
> index 0000000..f0b3181
> --- /dev/null
> +++ b/stdlib/getrandom.c
> @@ -0,0 +1,31 @@
> +/* Stub for 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 <sys/random.h>
> +
> +/* Write LENGTH bytes of randomness starting at BUFFER. Returns the
> + number of bytes written, or -1 on error. */
> +ssize_t
> +__getrandom (void *buffer, size_t length, unsigned int flags)
> +{
> + __set_errno (ENOSYS);
> + return -1;
> +}
> +
> +stub_warning (__getrandom)
> diff --git a/stdlib/sys/random.h b/stdlib/sys/random.h
> new file mode 100644
> index 0000000..912807c
> --- /dev/null
> +++ b/stdlib/sys/random.h
> @@ -0,0 +1,35 @@
> +/* Interfaces for obtaining random bytes.
> + 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/>. */
> +
> +#ifndef _SYS_RANDOM_H
> +#define _SYS_RANDOM_H 1
> +
> +/* Flags for use with getrandom. */
> +# define GRND_NONBLOCK 1
> +# define GRND_RANDOM 2
> +
> +/* Write LENGTH bytes of randomness starting at BUFFER. Returns the
> + number of bytes written, or -1 on error. */
> +ssize_t __getrandom (void *__buffer, size_t __length, unsigned int __flags)
> + __THROW __wur;
> +
> +/* Prevent accidental interposition of the getrandom symbol. */
> +#define getrandom(buffer, length, flags) \
Should this be indented?
> + (0 + __getrandom (buffer, length, flags))
> +
> +#endif /* _SYS_RANDOM_H */
> diff --git a/stdlib/tst-getrandom.c b/stdlib/tst-getrandom.c
> new file mode 100644
> index 0000000..2e4f7e7
> --- /dev/null
> +++ b/stdlib/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 <sys/random.h>
> +
> +/* Set to true if any errors are encountered. */
> +static bool errors;
> +
> +/* Test getrandom with a single buffer length. */
> +static void
> +test_length (char *buffer, int length, unsigned flags)
I know this works, but is there a reason for using char *, int, and
unsigned instead of void *, size_t, and unsigned int? test_flags below
also uses "unsigned".
> +{
> + 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;
> + }
> + }
Same error message in both cases.
> + 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 spurious bytes\n",
> + length, flags);
> + errors = true;
> + }
> +}
> +
> +/* Call getrandom repeatedly to fill 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))
Should be buffer1 and buffer2, I imagine.
> + {
> + 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"
<abilist snipped>
> diff --git a/sysdeps/posix/getrandom.c b/sysdeps/posix/getrandom.c
> new file mode 100644
> index 0000000..030d8cb
> --- /dev/null
> +++ b/sysdeps/posix/getrandom.c
> @@ -0,0 +1,27 @@
> +/* 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"
> +
> +/* Write LENGTH bytes of randomness starting at BUFFER. Returns the
> + number of bytes written, or -1 on error. */
> +ssize_t
> +__getrandom (void *buffer, size_t length, unsigned int flags)
> +{
> + return getrandom_emulation (buffer, length, flags);
> +}
> diff --git a/sysdeps/posix/getrandom_emulation.c b/sysdeps/posix/getrandom_emulation.c
> new file mode 100644
> index 0000000..75534d9
> --- /dev/null
> +++ b/sysdeps/posix/getrandom_emulation.c
> @@ -0,0 +1,111 @@
> +/* 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 <libc-lock.h>
> +#include <not-cancel.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +
> +/* Support flags by this emulation. Additional flags will cause the
> + emulation to fail with EINVAL. */
> +#define GETRANDOM_SUPPORTED_FLAGS (GRND_RANDOM | GRND_NONBLOCK)
> +
> +/* Open the device node for the random device requested by FLAGS.
> + Return -1 on error (and set errno), and the file descriptor on
> + success. */
> +static int
> +getrandom_open_fd (int flags)
Should this be unsigned int, for consistency?
> +{
> + int open_flags = O_RDONLY | O_CLOEXEC;
> + if (flags & GRND_NONBLOCK)
> + open_flags |= O_NONBLOCK;
> + const char *device;
> + if (flags & GRND_RANDOM)
> + device = "/dev/random";
> + else
> + device = "/dev/urandom";
> + return open_not_cancel (device, open_flags, 0);
> +}
> +
> +/* Attempt to read LENGTH bytes from FD, avoiding short reads.
> + Intended for getrandom calls without any flags. */
> +static ssize_t
> +getrandom_read_fd (int fd, void *buffer, size_t length)
> +{
> + void *end = buffer + length;
> + while (buffer < end)
> + {
> + /* EINTR can occur without any flags during early userspace
> + initialization. */
> + ssize_t ret = TEMP_FAILURE_RETRY
> + (read_not_cancel (fd, buffer, end - buffer));
> + if (ret < 0)
> + /* Do not report the short read, return the error. */
> + return -1;
> + if (ret == 0)
> + __libc_fatal ("error: end of file on randomness device\n");
> + buffer += ret;
> + }
> + return length;
> +}
> +
> +static void
> +getrandom_close_fd (void *pfd)
> +{
> + close_not_cancel_no_status (*(int *) pfd);
> +}
> +
> +/* Write LENGTH bytes of randomness starting at BUFFER. Returns the
> + number of bytes written, or -1 on error. Implementation based on
> + emulation with /dev/urandom and /dev/random. */
> +static ssize_t
> +getrandom_emulation (void *buffer, size_t length, unsigned int flags)
> +{
> + /* Check if any unsupported flags have been requested. */
> + if (flags & ~GETRANDOM_SUPPORTED_FLAGS)
> + {
> + __set_errno (EINVAL);
> + return -1;
> + }
> +
> + int fd = getrandom_open_fd (flags);
> + if (fd < 0)
> + return -1;
> +
> + /* If flags is zero, avoid short reads. */
> + if (flags == 0)
> + {
> + ssize_t ret = getrandom_read_fd (fd, buffer, length);
> + close_not_cancel_no_status (fd);
> + return ret;
> + }
> + else
> + {
> + /* Some flags are set. Allow cancellation, and pass short reads
> + to the caller. */
> + ssize_t ret;
> + __libc_cleanup_push (getrandom_close_fd, &fd);
> + ret = __read (fd, buffer, length);
> + __libc_cleanup_pop (1);
> + return ret;
> + }
> +}
<abilist snipped>
> diff --git a/sysdeps/unix/sysv/linux/getrandom.c b/sysdeps/unix/sysv/linux/getrandom.c
> new file mode 100644
> index 0000000..bab2774
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/getrandom.c
> @@ -0,0 +1,164 @@
> +/* 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 <assert.h>
> +#include <errno.h>
> +#include <kernel-features.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/random.h>
> +#include <sysdep-cancel.h>
> +#include <unistd.h>
> +
> +#ifdef __NR_getrandom
> +
> +/* getrandom system call wrapper with no flags, and which can only
> + process INT_MAX bytes at a time, and retries on EINTR. */
> +static int
> +getrandom_syscall_intmax (void *buffer, size_t length)
> +{
> + return TEMP_FAILURE_RETRY (INLINE_SYSCALL (getrandom, 3, buffer, length, 0));
> +}
> +
> +/* getrandom system call wrapper with no flags, and which can process
> + all lengths and retries on EINTR. */
> +static ssize_t
> +getrandom_syscall_no_flags (void *buffer, size_t length)
> +{
> + void *p = buffer;
> + void *end = buffer + length;
> +
> + /* Execute the system call even for length == 0, so that we properly
> + report 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 getrandom_result = getrandom_syscall_intmax (p, to_get);
> + /* Stop on error. Do not report the short read, return the
> + error. */
> + if (getrandom_result < 0)
> + return -1;
> + /* If the system call returns 0 for some reason, we would enter
> + an infinite loop. */
> + assert (getrandom_result > 0 || length == 0);
> + p += getrandom_result;
> + }
> + while (p < end);
> +
> + return length;
> +}
> +
> +/* getrandom system call wrapper with special support for FLAGS == 0
> + (EINTR retry, arbitrary lengths). */
> +static ssize_t
> +getrandom_try_syscall (void *buffer, size_t length, unsigned int flags)
> +{
> + if (flags == 0)
> + return getrandom_syscall_no_flags (buffer, length);
> + else
> + return SYSCALL_CANCEL (getrandom, buffer, length, flags);
> +}
> +
> +/* Same as getrandom_try_syscall, but terminate the process on an
> + ENOSYS error. */
> +static ssize_t
> +getrandom_force_syscall (void *buffer, size_t length, unsigned int flags)
> +{
> + ssize_t ret = getrandom_try_syscall (buffer, length, flags);
> + if (ret < 0 && ret == ENOSYS)
> + __libc_fatal ("error: getrandom system call failed with ENONSYS\n");
> + return ret;
> +}
> +
> +# ifdef __ASSUME_GETRANDOM_SYSCALL
> +/* Write LENGTH bytes of randomness starting at BUFFER. Returns the
> + number of bytes written, or -1 on error. This implementation uses
> + the system call unconditionally and terminates the process if it
> + fails with ENOSYS. */
> +ssize_t
> +__getrandom (void *buffer, size_t length, unsigned int flags)
> +{
> + return getrandom_force_syscall (buffer, length, flags);
> +}
> +
> +# 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;
> +
> +/* Write LENGTH bytes of randomness starting at BUFFER. Returns the
> + number of bytes written, or -1 on error. This implementation falls
> + back to emulation in case the system call is unavailable. */
> +ssize_t
> +__getrandom (void *buffer, size_t length, unsigned int 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_try_syscall (buffer, length, flags);
> + if (ret < 0 && errno == ENOSYS)
> + {
> + /* Record that the system call is missing 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_force_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"
> +
> +/* Write LENGTH bytes of randomness starting at BUFFER. Returns the
> + number of bytes written, or -1 on error. This implementation uses
> + emulation with device nodes. */
> +ssize_t
> +__getrandom (void *buffer, size_t length, unsigned int flags)
> +{
> + return getrandom_emulation (buffer, length, flags);
> +}
> +
> +#endif
<abilist snipped>
Rical
More information about the Libc-alpha
mailing list