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 }