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]

Re: [PATCH v2] Add getrandom implementation [BZ #17252]


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


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