(root)/
libxcrypt-4.4.36/
test/
getrandom-interface.c
       1  /* Test the exposed interface of 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 <errno.h>
      13  #include <setjmp.h>
      14  #include <stdio.h>
      15  #include <string.h>
      16  #include <signal.h>
      17  #include <sys/mman.h>
      18  
      19  static bool error_occurred;
      20  
      21  /* Note: both of the following test functions expect PAGE to point to
      22     PAGESIZE bytes of read-write memory followed by another PAGESIZE
      23     bytes of unwritable memory.  Both functions also assume that
      24     PAGESIZE is greater than or equal to 256.  */
      25  
      26  static void
      27  test_basic (char *page, size_t pagesize)
      28  {
      29    printf ("Testing basic functionality...\n");
      30  
      31    // A request for zero bytes should succeed, and should not touch the
      32    // output buffer.
      33    if (!get_random_bytes (page + pagesize, 0))
      34      {
      35        printf ("ERROR: get_random_bytes(0) = %s\n", strerror (errno));
      36        error_occurred = 1;
      37      }
      38    else
      39      printf ("ok: get_random_bytes(0)\n");
      40  
      41    // A request for 257 bytes should fail, and should not touch the
      42    // output buffer.
      43    if (get_random_bytes (page + pagesize, 257))
      44      {
      45        printf ("ERROR: get_random_bytes(257) succeeded\n");
      46        error_occurred = 1;
      47      }
      48    else if (errno != EIO)
      49      {
      50        printf ("ERROR: get_random_bytes(257) = %s (expected: %s)\n",
      51                strerror (errno), strerror (EIO));
      52        error_occurred = 1;
      53      }
      54    else
      55      printf ("ok: get_random_bytes(257)\n");
      56  
      57    // A request for five bytes should succeed, and should not write
      58    // past the end of the buffer.  (We use an odd, prime number here to
      59    // catch implementations that might write e.g. four or eight bytes
      60    // at once.)
      61    if (!get_random_bytes (page + pagesize - 5, 5))
      62      {
      63        printf ("ERROR: get_random_bytes(5) = %s\n", strerror (errno));
      64        error_occurred = 1;
      65      }
      66    else
      67      printf ("ok: get_random_bytes(5)\n");
      68  
      69    // It's extremely difficult to say whether any output of a random
      70    // number generator is or is not "good", but the odds that 251 bytes
      71    // of RNG output are all zero is one in 2**2008, and the odds that
      72    // the first 251 bytes of RNG output are equal to the second 251
      73    // bytes of RNG output is also one in 2**2008.  (Again, we use an
      74    // odd, prime number to trip up implementations that do wide writes.)
      75  
      76    char prev[251];
      77    memset (prev, 0, 251);
      78  
      79    if (!get_random_bytes (page + pagesize - 251, 251))
      80      {
      81        printf ("ERROR: get_random_bytes(251)/1 = %s\n", strerror (errno));
      82        error_occurred = 1;
      83        return;
      84      }
      85  
      86    if (!memcmp (prev, page + pagesize - 251, 251))
      87      {
      88        printf ("ERROR: get_random_bytes(251)/1 produced all zeroes\n");
      89        error_occurred = 1;
      90        return;
      91      }
      92  
      93    memcpy (prev, page + pagesize - 251, 251);
      94  
      95    if (!get_random_bytes (page + pagesize - 251, 251))
      96      {
      97        printf ("ERROR: get_random_bytes(251)/2 = %s\n", strerror (errno));
      98        error_occurred = 1;
      99        return;
     100      }
     101  
     102    if (!memcmp (prev, page + pagesize - 251, 251))
     103      {
     104        printf ("ERROR: get_random_bytes(251)/2 produced same output "
     105                "as /1\n");
     106        error_occurred = 1;
     107        return;
     108      }
     109  
     110    printf ("ok: get_random_bytes(251) smoke test of output\n");
     111  }
     112  
     113  static void
     114  test_fault (char *page, size_t pagesize)
     115  {
     116    printf ("Testing partially inaccessible output buffer...\n");
     117    bool rv = get_random_bytes (page + pagesize - 64, 128);
     118    /* shouldn't ever get here */
     119    error_occurred = 1;
     120    if (rv)
     121      printf ("ERROR: success (should have faulted)\n");
     122    else
     123      printf ("ERROR: failed with %s (should have faulted)\n",
     124              strerror (errno));
     125  }
     126  
     127  /* In one of the tests above, a segmentation fault is the expected result.  */
     128  static sigjmp_buf env;
     129  static void
     130  segv_handler (int sig)
     131  {
     132    siglongjmp (env, sig);
     133  }
     134  
     135  static void
     136  expect_no_fault (char *page, size_t pagesize,
     137                   void (*testfn) (char *, size_t))
     138  {
     139    int rv = sigsetjmp (env, 1);
     140    if (!rv)
     141      testfn (page, pagesize);
     142    else
     143      {
     144        printf ("ERROR: Unexpected %s\n", strsignal (rv));
     145        error_occurred = 1;
     146      }
     147  }
     148  
     149  static void
     150  expect_a_fault (char *page, size_t pagesize,
     151                  void (*testfn) (char *, size_t))
     152  {
     153    int rv = sigsetjmp (env, 1);
     154    if (!rv)
     155      {
     156        testfn (page, pagesize);
     157        printf ("ERROR: No signal occurred\n");
     158        error_occurred = 1;
     159      }
     160    else
     161      {
     162        printf ("ok: %s (as expected)\n", strsignal (rv));
     163      }
     164  }
     165  
     166  int
     167  main (void)
     168  {
     169    /* Set up a two-page region whose first page is read-write and
     170       whose second page is inaccessible.  */
     171    long pagesize_l = sysconf (_SC_PAGESIZE);
     172    if (pagesize_l < 256)
     173      {
     174        printf ("ERROR: pagesize of %ld is too small\n", pagesize_l);
     175        return 99;
     176      }
     177  
     178    size_t pagesize = (size_t) pagesize_l;
     179    char *page = mmap (0, pagesize * 2, PROT_READ|PROT_WRITE,
     180                       MAP_PRIVATE|MAP_ANON, -1, 0);
     181    if (page == MAP_FAILED)
     182      {
     183        perror ("mmap");
     184        return 1;
     185      }
     186    memset (page, 'x', pagesize * 2);
     187    if (mprotect (page + pagesize, pagesize, PROT_NONE))
     188      {
     189        perror ("mprotect");
     190        return 1;
     191      }
     192  
     193    struct sigaction sa, os, ob;
     194    sigfillset (&sa.sa_mask);
     195    sa.sa_flags = SA_RESTART;
     196    sa.sa_handler = segv_handler;
     197    if (sigaction (SIGBUS, &sa, &ob) || sigaction (SIGSEGV, &sa, &os))
     198      {
     199        perror ("sigaction");
     200        return 1;
     201      }
     202  
     203    expect_no_fault (page, pagesize, test_basic);
     204    expect_a_fault  (page, pagesize, test_fault);
     205  
     206    sigaction (SIGBUS, &ob, 0);
     207    sigaction (SIGSEGV, &os, 0);
     208  
     209    return error_occurred;
     210  }