[PATCH 2/2] linux: use __getrandom_nocancel in getentropy

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Wed Feb 12 12:57:44 GMT 2025



On 09/02/25 00:14, Cristian Rodríguez wrote:
> On Sat, Feb 8, 2025 at 2:05 PM Cristian Rodríguez <cristian@rodriguez.im> wrote:
>>
>> It must use the VDSO implementation if available instead of a raw
>> syscall.
>>
>> Signed-off-by: Cristian Rodríguez <cristian@rodriguez.im>
> 
> 
> I looked at the other implementations around and needs this needs to
> 
> - be made POSIX.2024 compatible, where the only possible errors are
> EINVAL and maybe ENOSYS.
> - The different BSDs implementations where this function originally
> comes from all abort on irrecoverable errors, either by explicit
> raise(SIGKILL) or because getentropy is just a wrapper against
> arc4random_buf
> 
> I got to the poin that I think it has just ot be implemented like this now:
> 
> int
> getentropy (void *buffer, size_t length)
> {
>   /* The interface is documented to return EINVAL for buffer lengths
>      longer than 256 bytes.  */
>   if (length > GETENTROPY_MAX)
>     {
>       __set_errno (EINVAL);
>       return -1;
>     }
>     __arc4random_buf(buffer, length);
>     return 0;
> }
> 
> Will that fly here ?

It would change getentropy semantics slight, since arc4random will try
to open/read/close the /dev/urandom if getrandom is not available (due
syscall filtering or old kernels).  In this case with current code, 
getentropy() will fail with ENOSYS; while for arc4random() it will abort() 
if /dev/urandom is not available or if it can't be open (ENFILE, etc.). 

I am not sure if this is problem, but I think it would be safe to avoid
changing for now.

Now that we have __getrandom_nocancel, we can just do something like:

static void
getentropy_fatal (void)
{
  __libc_fatal ("Fatal glibc error: cannot get entropy for getentropy\n");
}

/* Write LENGTH bytes of randomness starting at BUFFER.  Return 0 on
   success and -1 on failure.  */
int
getentropy (void *buffer, size_t length)
{
  if (length > 256)
    {
      __set_errno (EINVAL);
      return -1;
    }

  /* Try to fill the buffer completely.  Even with the 256 byte limit
     above, we might still receive an EINTR error (when blocking
     during boot).  */
  void *end = buffer + length;
  while (buffer < end)
    {
      /* NB: No cancellation point.  */
      ssize_t bytes = __getrandom_nocancel (buffer, end - buffer, 0);
      if (bytes < 0)
        {
          switch (errno)
            {
            case EINTR:
              continue;
            case ENOSYS:
              return -1;
            default:
              getentropy_fatal ();
            }
        }
      else if (bytes == 0)
        /* No more bytes available.  This should not happen under normal
           circumstances.  */
        getentropy_fatal ();

      /* Try again in case of a short read.  */
      buffer += bytes;
    }
  return 0;
}

And remove both sysdeps/mach/hurd/getentropy.c and sysdeps/unix/sysv/linux/getentropy.c





More information about the Libc-alpha mailing list