From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 88444 invoked by alias); 8 Sep 2016 11:58:24 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Received: (qmail 88432 invoked by uid 89); 8 Sep 2016 11:58:22 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.0 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_NONE,RP_MATCHES_RCVD,SPF_PASS autolearn=ham version=3.3.2 spammy=Hx-spam-relays-external:sk:mail.pa, H*RU:sk:mail.pa, H*r:sk:mail.pa, HX-HELO:sk:mail.pa X-HELO: mail.pacific.net Subject: Re: [PATCH v2] Add getrandom implementation [BZ #17252] To: Florian Weimer References: <88371300-c533-9886-f1de-e34f17f7cbb4@redhat.com> <5774E713.5000907@pacific.net> <700f4f79-2ef1-c34b-2ce9-190e5899fa64@redhat.com> From: Rical Jasan Cc: libc-alpha Message-ID: <062cdb3f-ebca-30e1-9204-9de36feefc20@pacific.net> Date: Thu, 08 Sep 2016 11:58:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.2.0 MIME-Version: 1.0 In-Reply-To: <700f4f79-2ef1-c34b-2ce9-190e5899fa64@redhat.com> Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 8bit X-Null-Tag: 497adeaa02c1223ca8fbbe95aac3a7f8 X-SW-Source: 2016-09/txt/msg00119.txt.bz2 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 > > [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 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 > + . */ > + > +#include > +#include > + > +/* 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 > + . */ > + > +#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 > + . */ > + > +#include > +#include > +#include > +#include > +#include > + > +/* 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" > 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 > + . */ > + > +#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 > + . */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* 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; > + } > +} > 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 > + . */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 Rical