(root)/
libxcrypt-4.4.36/
test/
getrandom-fallbacks.c
       1  /* Test the fallback logic in get_random_bytes.
       2  
       3     Written by Zack Weinberg <zackw at panix.com> in 2018.
       4     To the extent possible under law, Zack Weinberg has waived all
       5     copyright and related or neighboring rights to this work.
       6  
       7     See https://creativecommons.org/publicdomain/zero/1.0/ for further
       8     details.  */
       9  
      10  #include "crypt-port.h"
      11  
      12  #include <stdarg.h>
      13  #include <stdio.h>
      14  #include <string.h>
      15  #include <errno.h>
      16  
      17  #ifdef HAVE_FCNTL_H
      18  #include <fcntl.h>
      19  #endif
      20  #if defined HAVE_SYS_SYSCALL_H
      21  #include <sys/syscall.h>
      22  #endif
      23  #ifdef HAVE_SYS_STAT_H
      24  #include <sys/stat.h>
      25  #endif
      26  
      27  /* If arc4random_buf is available, all of the fallback logic is compiled
      28     out and this test is unnecessary.  If ld --wrap is not available this
      29     test will not work.  */
      30  #if defined HAVE_ARC4RANDOM_BUF || !defined HAVE_LD_WRAP
      31  
      32  int
      33  main (void)
      34  {
      35    return 77;
      36  }
      37  
      38  #else
      39  
      40  /* All of the mock system primitives below fill in their buffer with
      41     repeats of these bytes, so we can tell where the data came from.  */
      42  #define MOCK_getentropy     'e'
      43  #define MOCK_getrandom      'r'
      44  #define MOCK_sys_getentropy 'E'
      45  #define MOCK_sys_getrandom  'R'
      46  #define MOCK_urandom        'u'
      47  
      48  #ifdef HAVE_GETENTROPY
      49  static bool getentropy_should_fail = false;
      50  extern int __wrap_getentropy (void *, size_t);
      51  int
      52  __wrap_getentropy (void *buf, size_t buflen)
      53  {
      54    if (getentropy_should_fail)
      55      {
      56        errno = ENOSYS;
      57        return -1;
      58      }
      59    else
      60      {
      61        memset (buf, MOCK_getentropy, buflen);
      62        return 0;
      63      }
      64  }
      65  #endif
      66  
      67  #ifdef HAVE_GETRANDOM
      68  static bool getrandom_should_fail = false;
      69  extern ssize_t __wrap_getrandom (void *, size_t, unsigned int);
      70  ssize_t
      71  __wrap_getrandom (void *buf, size_t buflen, unsigned int ARG_UNUSED(flags))
      72  {
      73    if (getrandom_should_fail)
      74      {
      75        errno = ENOSYS;
      76        return -1;
      77      }
      78    else
      79      {
      80        buflen = MIN (buflen, INT16_MAX);
      81        memset (buf, MOCK_getrandom, buflen);
      82        return (ssize_t)buflen;
      83      }
      84  }
      85  #endif
      86  
      87  #ifdef HAVE_SYSCALL
      88  #ifdef SYS_getentropy
      89  static bool sys_getentropy_should_fail = false;
      90  #endif
      91  #ifdef SYS_getrandom
      92  static bool sys_getrandom_should_fail = false;
      93  #endif
      94  static bool other_syscalls = false;
      95  extern long __wrap_syscall (long, ...);
      96  long
      97  __wrap_syscall(long number, ...)
      98  {
      99  #ifdef SYS_getentropy
     100    if (number == SYS_getentropy)
     101      {
     102        if (sys_getentropy_should_fail)
     103          {
     104            errno = ENOSYS;
     105            return -1;
     106          }
     107        else
     108          {
     109            va_list ap;
     110            va_start (ap, number);
     111            void *buf = va_arg (ap, void *);
     112            size_t buflen = va_arg (ap, size_t);
     113            va_end (ap);
     114            memset (buf, MOCK_sys_getentropy, buflen);
     115            return 0;
     116          }
     117      }
     118  #endif
     119  #ifdef SYS_getrandom
     120    if (number == SYS_getrandom)
     121      {
     122        if (sys_getrandom_should_fail)
     123          {
     124            errno = ENOSYS;
     125            return -1;
     126          }
     127        else
     128          {
     129            va_list ap;
     130            va_start (ap, number);
     131            void *buf = va_arg (ap, void *);
     132            size_t buflen = va_arg (ap, size_t);
     133            buflen = MIN (buflen, INT16_MAX);
     134            va_end (ap);
     135            memset (buf, MOCK_sys_getrandom, buflen);
     136            return (ssize_t)buflen;
     137          }
     138      }
     139  #endif
     140    /* There is no vsyscall.  We just have to hope nobody in this test
     141       program wants to use syscall() for anything else.  */
     142    other_syscalls = true;
     143    fprintf (stderr, "ERROR: unexpected syscall(%ld)\n", number);
     144    errno = ENOSYS;
     145    return -1;
     146  }
     147  #endif /* HAVE_SYSCALL */
     148  
     149  /* It is not possible to hit both of the code paths that can set the
     150     "/dev/urandom doesn't work" flag in a single test program, because
     151     there's no way to _clear_ that flag again.  This test chooses to
     152     exercise the read-failure path, not the open-failure path.  */
     153  #if defined HAVE_SYS_STAT_H && defined HAVE_FCNTL_H && defined HAVE_UNISTD_H
     154  static bool urandom_should_fail = false;
     155  static int urandom_fd = -1;
     156  extern int __wrap_open (const char *, int, mode_t);
     157  extern int __real_open (const char *, int, mode_t);
     158  int
     159  __wrap_open (const char *path, int flags, mode_t mode)
     160  {
     161    int ret = __real_open (path, flags, mode);
     162    if (ret == -1)
     163      return ret;
     164    if (!strcmp (path, "/dev/urandom"))
     165      urandom_fd = ret;
     166    return ret;
     167  }
     168  
     169  #ifdef HAVE_OPEN64
     170  extern int __wrap_open64 (const char *, int, mode_t);
     171  extern int __real_open64 (const char *, int, mode_t);
     172  int
     173  __wrap_open64 (const char *path, int flags, mode_t mode)
     174  {
     175    int ret = __real_open64 (path, flags, mode);
     176    if (ret == -1)
     177      return ret;
     178    if (!strcmp (path, "/dev/urandom"))
     179      urandom_fd = ret;
     180    return ret;
     181  }
     182  #endif
     183  
     184  extern int __wrap_close (int);
     185  extern int __real_close (int);
     186  int
     187  __wrap_close (int fd)
     188  {
     189    if (fd == urandom_fd)
     190      urandom_fd = -1;
     191    return __real_close (fd);
     192  }
     193  
     194  extern ssize_t __wrap_read (int, void *, size_t);
     195  extern ssize_t __real_read (int, void *, size_t);
     196  ssize_t
     197  __wrap_read (int fd, void *buf, size_t count)
     198  {
     199    if (fd == urandom_fd)
     200      {
     201        if (urandom_should_fail)
     202          {
     203            errno = ENOSYS;
     204            return -1;
     205          }
     206        else
     207          {
     208            count = MIN (count, INT16_MAX);
     209            memset (buf, MOCK_urandom, count);
     210            return (ssize_t)count;
     211          }
     212      }
     213    else
     214      return __real_read (fd, buf, count);
     215  }
     216  
     217  #endif
     218  
     219  struct subtest
     220  {
     221    const char *what;
     222    bool *make_fail;
     223    char expected;
     224  };
     225  const struct subtest subtests[] =
     226  {
     227    { "initial", 0, 'x' },
     228  
     229  #ifdef HAVE_GETENTROPY
     230    { "getentropy", &getentropy_should_fail, MOCK_getentropy },
     231  #endif
     232  #ifdef HAVE_GETRANDOM
     233    { "getrandom", &getrandom_should_fail, MOCK_getrandom },
     234  #endif
     235  
     236  #ifdef HAVE_SYSCALL
     237  #ifdef SYS_getentropy
     238    { "sys_getentropy", &sys_getentropy_should_fail, MOCK_sys_getentropy },
     239  #endif
     240  #ifdef SYS_getrandom
     241    { "sys_getrandom", &sys_getrandom_should_fail, MOCK_sys_getrandom },
     242  #endif
     243  #endif
     244  
     245  #if defined HAVE_SYS_STAT_H && defined HAVE_FCNTL_H && defined HAVE_UNISTD_H
     246    { "/dev/urandom", &urandom_should_fail, MOCK_urandom },
     247  #endif
     248  
     249    { "final", 0, 0 }
     250  };
     251  
     252  int
     253  main (void)
     254  {
     255    char buf[257];
     256    char expected[2] = { 0, 0 };
     257    memset (buf, 'x', sizeof buf - 1);
     258    buf[sizeof buf - 1] = '\0';
     259    bool failed = false;
     260    const struct subtest *s;
     261  
     262    for (s = subtests; s->expected;)
     263      {
     264        expected[0] = s->expected;
     265        if (strspn (buf, expected) != 256)
     266          {
     267            printf ("FAIL: %s: buffer not filled with '%c'\n",
     268                    s->what, s->expected);
     269            failed = true;
     270          }
     271        else
     272          printf ("ok: %s (output)\n", s->what);
     273  
     274        if (s->make_fail)
     275          *(s->make_fail) = true;
     276        s++;
     277  
     278        bool r = get_random_bytes (buf, sizeof buf - 1);
     279        buf[sizeof buf - 1] = '\0';
     280        if ((s->expected && !r) || (!s->expected && r))
     281          {
     282            printf ("FAIL: %s: get_random_bytes: %s\n",
     283                    s->what, strerror (errno));
     284            failed = true;
     285          }
     286        else
     287          printf ("ok: %s (return)\n", s->what);
     288      }
     289  #if HAVE_SYSCALL
     290    failed |= other_syscalls;
     291  #endif
     292    return failed;
     293  }
     294  
     295  #endif