(root)/
glibc-2.38/
sysdeps/
mach/
hurd/
getrandom.c
       1  /* Hurdish implementation of getrandom
       2     Copyright (C) 2016-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <hurd.h>
      20  #include <sys/random.h>
      21  #include <fcntl.h>
      22  
      23  __libc_rwlock_define_initialized (static, lock);
      24  static file_t random_server, random_server_nonblock,
      25                urandom_server, urandom_server_nonblock;
      26  
      27  extern char *__trivfs_server_name __attribute__((weak));
      28  
      29  /* Write up to LENGTH bytes of randomness starting at BUFFER.
      30     Return the number of bytes written, or -1 on error.  */
      31  ssize_t
      32  __getrandom (void *buffer, size_t length, unsigned int flags)
      33  {
      34    const char *random_source = "/dev/urandom";
      35    int open_flags = O_RDONLY;
      36    file_t server, *cached_server;
      37    error_t err;
      38    char *data = buffer;
      39    mach_msg_type_number_t nread = length;
      40  
      41    switch (flags)
      42      {
      43      case 0:
      44        cached_server = &urandom_server;
      45        break;
      46      case GRND_RANDOM:
      47        cached_server = &random_server;
      48        break;
      49      case GRND_NONBLOCK:
      50        cached_server = &urandom_server_nonblock;
      51        break;
      52      case GRND_RANDOM | GRND_NONBLOCK:
      53        cached_server = &random_server_nonblock;
      54        break;
      55      default:
      56        return __hurd_fail (EINVAL);
      57      }
      58  
      59    if (flags & GRND_RANDOM)
      60      random_source = "/dev/random";
      61    if (flags & GRND_NONBLOCK)
      62      open_flags |= O_NONBLOCK;
      63    /* No point in passing either O_NOCTTY, O_IGNORE_CTTY, or O_CLOEXEC
      64       to file_name_lookup, since we're not making an fd.  */
      65  
      66    if (&__trivfs_server_name && __trivfs_server_name
      67        && __trivfs_server_name[0] == 'r'
      68        && __trivfs_server_name[1] == 'a'
      69        && __trivfs_server_name[2] == 'n'
      70        && __trivfs_server_name[3] == 'd'
      71        && __trivfs_server_name[4] == 'o'
      72        && __trivfs_server_name[5] == 'm'
      73        && __trivfs_server_name[6] == '\0')
      74      /* We are random, don't try to read ourselves!  */
      75      return length;
      76  
      77  again:
      78    __libc_rwlock_rdlock (lock);
      79    server = *cached_server;
      80    if (MACH_PORT_VALID (server))
      81      /* Attempt to read some random data using this port.  */
      82      err = __io_read (server, &data, &nread, -1, length);
      83    else
      84      err = MACH_SEND_INVALID_DEST;
      85    __libc_rwlock_unlock (lock);
      86  
      87    if (err == MACH_SEND_INVALID_DEST || err == MIG_SERVER_DIED)
      88      {
      89        file_t oldserver = server;
      90        mach_port_urefs_t urefs;
      91  
      92        /* Slow path: the cached port didn't work, or there was no
      93           cached port in the first place.  */
      94  
      95        __libc_rwlock_wrlock (lock);
      96        server = *cached_server;
      97        if (server != oldserver)
      98          {
      99            /* Someone else must have refetched the port while we were
     100               waiting for the lock. */
     101            __libc_rwlock_unlock (lock);
     102            goto again;
     103          }
     104  
     105        if (MACH_PORT_VALID (server))
     106          {
     107            /* It could be that someone else has refetched the port and
     108               it got the very same name.  So check whether it is a send
     109               right (and not a dead name).  */
     110            err = __mach_port_get_refs (__mach_task_self (), server,
     111                                        MACH_PORT_RIGHT_SEND, &urefs);
     112            if (!err && urefs > 0)
     113              {
     114                __libc_rwlock_unlock (lock);
     115                goto again;
     116              }
     117  
     118            /* Now we're sure that it's dead.  */
     119            __mach_port_deallocate (__mach_task_self (), server);
     120          }
     121  
     122        server = *cached_server = __file_name_lookup (random_source,
     123                                                      open_flags, 0);
     124        __libc_rwlock_unlock (lock);
     125        if (!MACH_PORT_VALID (server))
     126  	{
     127  	  if (errno == ENOENT)
     128  	    /* No translator set up, we won't have support for it.  */
     129  	    errno = ENOSYS;
     130  	  /* No luck.  */
     131  	  return -1;
     132  	}
     133  
     134        goto again;
     135      }
     136  
     137    if (err)
     138      return __hurd_fail (err);
     139  
     140    if (data != buffer)
     141      {
     142        if (nread > length)
     143          {
     144            __vm_deallocate (__mach_task_self (), (vm_address_t) data, nread);
     145            return __hurd_fail (EGRATUITOUS);
     146          }
     147        memcpy (buffer, data, nread);
     148        __vm_deallocate (__mach_task_self (), (vm_address_t) data, nread);
     149      }
     150  
     151    return nread;
     152  }
     153  
     154  libc_hidden_def (__getrandom)
     155  weak_alias (__getrandom, getrandom)