1 /* Copyright (c) 2019, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include <gtest/gtest.h>
16 #include <stdlib.h>
17
18 #include <openssl/rand.h>
19
20 #include "internal.h"
21
22
23 #if defined(OPENSSL_X86_64) && defined(OPENSSL_LINUX) && \
24 !defined(BORINGSSL_SHARED_LIBRARY) && \
25 !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
26
27 #include <linux/random.h>
28 #include <sys/ptrace.h>
29 #include <sys/syscall.h>
30 #include <sys/user.h>
31
32 #if defined(OPENSSL_NO_ASM)
have_rdrand()33 static int have_rdrand() { return 0; }
34 #endif
35
36 // This test can be run with $OPENSSL_ia32cap=~0x4000000000000000 in order to
37 // simulate the absence of RDRAND of machines that have it.
38
39 // Event represents a system call from urandom.c that is observed by the ptrace
40 // code in |GetTrace|.
41 struct Event {
42 enum class Syscall {
43 kGetRandom,
44 kOpen,
45 kUrandomRead,
46 kUrandomIoctl,
47 kAbort,
48 };
49
EventEvent50 explicit Event(Syscall syscall) : type(syscall) {}
51
operator ==Event52 bool operator==(const Event &other) const {
53 return type == other.type && length == other.length &&
54 flags == other.flags &&
55 ((filename == nullptr && other.filename == nullptr) ||
56 strcmp(filename, other.filename) == 0);
57 }
58
GetRandomEvent59 static Event GetRandom(size_t length, unsigned flags) {
60 Event e(Syscall::kGetRandom);
61 e.length = length;
62 e.flags = flags;
63 return e;
64 }
65
OpenEvent66 static Event Open(const char *filename) {
67 Event e(Syscall::kOpen);
68 e.filename = filename;
69 return e;
70 }
71
UrandomReadEvent72 static Event UrandomRead(size_t length) {
73 Event e(Syscall::kUrandomRead);
74 e.length = length;
75 return e;
76 }
77
UrandomIoctlEvent78 static Event UrandomIoctl() {
79 Event e(Syscall::kUrandomIoctl);
80 return e;
81 }
82
AbortEvent83 static Event Abort() {
84 Event e(Syscall::kAbort);
85 return e;
86 }
87
StringEvent88 std::string String() const {
89 char buf[256];
90
91 switch (type) {
92 case Syscall::kGetRandom:
93 snprintf(buf, sizeof(buf), "getrandom(_, %zu, %d)", length, flags);
94 break;
95
96 case Syscall::kOpen:
97 snprintf(buf, sizeof(buf), "open(%s, _)", filename);
98 break;
99
100 case Syscall::kUrandomRead:
101 snprintf(buf, sizeof(buf), "read(urandom_fd, _, %zu)", length);
102 break;
103
104 case Syscall::kUrandomIoctl:
105 return "ioctl(urandom_fd, RNDGETENTCNT, _)";
106
107 case Syscall::kAbort:
108 return "abort()";
109 }
110
111 return std::string(buf);
112 }
113
114 const Syscall type;
115 size_t length = 0;
116 unsigned flags = 0;
117 const char *filename = nullptr;
118 };
119
ToString(const std::vector<Event> & trace)120 static std::string ToString(const std::vector<Event> &trace) {
121 std::string ret;
122 for (const auto &event : trace) {
123 if (!ret.empty()) {
124 ret += ", ";
125 }
126 ret += event.String();
127 }
128 return ret;
129 }
130
131 // The following are flags to tell |GetTrace| to inject faults, using ptrace,
132 // into the entropy-related system calls.
133
134 // getrandom gives |ENOSYS|.
135 static const unsigned NO_GETRANDOM = 1;
136 // opening /dev/urandom fails.
137 static const unsigned NO_URANDOM = 2;
138 // getrandom always returns |EAGAIN| if given |GRNG_NONBLOCK|.
139 static const unsigned GETRANDOM_NOT_READY = 4;
140 // The ioctl on urandom returns only 255 bits of entropy the first time that
141 // it's called.
142 static const unsigned URANDOM_NOT_READY = 8;
143 // getrandom gives |EINVAL| unless |NO_GETRANDOM| is set.
144 static const unsigned GETRANDOM_ERROR = 16;
145 // Reading from /dev/urandom gives |EINVAL|.
146 static const unsigned URANDOM_ERROR = 32;
147 static const unsigned NEXT_FLAG = 64;
148
149 // GetTrace runs |thunk| in a forked process and observes the resulting system
150 // calls using ptrace. It simulates a variety of failures based on the contents
151 // of |flags| and records the observed events by appending to |out_trace|.
GetTrace(std::vector<Event> * out_trace,unsigned flags,std::function<void ()> thunk)152 static void GetTrace(std::vector<Event> *out_trace, unsigned flags,
153 std::function<void()> thunk) {
154 const int child_pid = fork();
155 ASSERT_NE(-1, child_pid);
156
157 if (child_pid == 0) {
158 // Child process
159 if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) {
160 perror("PTRACE_TRACEME");
161 _exit(1);
162 }
163 raise(SIGSTOP);
164 thunk();
165 _exit(0);
166 }
167
168 // Parent process
169 int status;
170 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
171 ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
172
173 // Set options so that:
174 // a) the child process is killed once this process dies.
175 // b) System calls result in a WSTOPSIG value of (SIGTRAP | 0x80) rather
176 // than just SIGTRAP. (This doesn't matter here, but it's recommended
177 // practice so that it's distinct from the signal itself.)
178 ASSERT_EQ(0, ptrace(PTRACE_SETOPTIONS, child_pid, nullptr,
179 PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD))
180 << strerror(errno);
181
182 // urandom_fd tracks the file descriptor number for /dev/urandom in the child
183 // process, if it opens it.
184 int urandom_fd = -1;
185
186 for (;;) {
187 // Advance the child to the next system call.
188 ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0));
189 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
190
191 // The child may have aborted rather than made a system call.
192 if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGABRT) {
193 out_trace->push_back(Event::Abort());
194 break;
195 }
196
197 // Otherwise the only valid ptrace event is a system call stop.
198 ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80));
199
200 struct user_regs_struct regs;
201 ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, ®s));
202 const auto syscall_number = regs.orig_rax;
203
204 bool is_opening_urandom = false;
205 bool is_urandom_ioctl = false;
206 uintptr_t ioctl_output_addr = 0;
207 // inject_error is zero to indicate that the system call should run
208 // normally. Otherwise it's, e.g. -EINVAL, to indicate that the system call
209 // should not run and that error should be injected on return.
210 int inject_error = 0;
211
212 switch (syscall_number) {
213 case __NR_getrandom:
214 if (flags & NO_GETRANDOM) {
215 inject_error = -ENOSYS;
216 } else if (flags & GETRANDOM_ERROR) {
217 inject_error = -EINVAL;
218 } else if (flags & GETRANDOM_NOT_READY) {
219 if (regs.rdx & GRND_NONBLOCK) {
220 inject_error = -EAGAIN;
221 }
222 }
223 out_trace->push_back(
224 Event::GetRandom(/*length=*/regs.rsi, /*flags=*/regs.rdx));
225 break;
226
227 case __NR_openat:
228 case __NR_open: {
229 // It's assumed that any arguments to open(2) are constants in read-only
230 // memory and thus the pointer in the child's context will also be a
231 // valid pointer in our address space.
232 const char *filename = reinterpret_cast<const char *>(
233 (syscall_number == __NR_openat) ? regs.rsi : regs.rdi);
234 out_trace->push_back(Event::Open(filename));
235 is_opening_urandom = strcmp(filename, "/dev/urandom") == 0;
236 if (is_opening_urandom && (flags & NO_URANDOM)) {
237 inject_error = -ENOENT;
238 }
239 break;
240 }
241
242 case __NR_read: {
243 const int read_fd = regs.rdi;
244 if (urandom_fd >= 0 && urandom_fd == read_fd) {
245 out_trace->push_back(Event::UrandomRead(/*length=*/regs.rdx));
246 if (flags & URANDOM_ERROR) {
247 inject_error = -EINVAL;
248 }
249 }
250 break;
251 }
252
253 case __NR_ioctl: {
254 const int ioctl_fd = regs.rdi;
255 if (urandom_fd >= 0 && ioctl_fd == urandom_fd &&
256 regs.rsi == RNDGETENTCNT) {
257 out_trace->push_back(Event::UrandomIoctl());
258 is_urandom_ioctl = true;
259 ioctl_output_addr = regs.rdx;
260 }
261 }
262 }
263
264 if (inject_error) {
265 // Replace the system call number with -1 to cause the kernel to ignore
266 // the call. The -ENOSYS will be replaced later with the value of
267 // |inject_error|.
268 regs.orig_rax = -1;
269 ASSERT_EQ(0, ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s));
270 }
271
272 ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0));
273 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
274 // If the system call was exit/exit_group, the process may be terminated
275 // rather than have exited the system call.
276 if (WIFEXITED(status)) {
277 ASSERT_EQ(0, WEXITSTATUS(status));
278 return;
279 }
280
281 // Otherwise the next state must be a system call exit stop. This is
282 // indistinguishable from a system call entry, we just have to keep track
283 // and know that these events happen in pairs.
284 ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80));
285
286 if (inject_error) {
287 if (inject_error != -ENOSYS) {
288 ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, ®s));
289 regs.rax = inject_error;
290 ASSERT_EQ(0, ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s));
291 }
292 } else if (is_opening_urandom) {
293 ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, ®s));
294 urandom_fd = regs.rax;
295 } else if (is_urandom_ioctl) {
296 // The result is the number of bits of entropy that the kernel currently
297 // believes that it has. urandom.c waits until 256 bits are ready.
298 int result = 256;
299
300 // If we are simulating urandom not being ready then we have the ioctl
301 // indicate one too few bits of entropy the first time it's queried.
302 if (flags & URANDOM_NOT_READY) {
303 result--;
304 flags &= ~URANDOM_NOT_READY;
305 }
306
307 // ptrace always works with ill-defined "words", which appear to be 64-bit
308 // on x86-64. Since the ioctl result is a 32-bit int, do a
309 // read-modify-write to inject the answer.
310 const uintptr_t aligned_addr = ioctl_output_addr & ~7;
311 const uintptr_t offset = ioctl_output_addr - aligned_addr;
312 union {
313 uint64_t word;
314 uint8_t bytes[8];
315 } u;
316 u.word = ptrace(PTRACE_PEEKDATA, child_pid,
317 reinterpret_cast<void *>(aligned_addr), nullptr);
318 memcpy(&u.bytes[offset], &result, sizeof(result));
319 ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, child_pid,
320 reinterpret_cast<void *>(aligned_addr),
321 reinterpret_cast<void *>(u.word)));
322 }
323 }
324 }
325
326 // TestFunction is the function that |GetTrace| is asked to trace.
TestFunction()327 static void TestFunction() {
328 uint8_t byte;
329 RAND_bytes(&byte, sizeof(byte));
330 RAND_bytes(&byte, sizeof(byte));
331 }
332
333 // TestFunctionPRNGModel is a model of how the urandom.c code will behave when
334 // |TestFunction| is run. It should return the same trace of events that
335 // |GetTrace| will observe the real code making.
TestFunctionPRNGModel(unsigned flags)336 static std::vector<Event> TestFunctionPRNGModel(unsigned flags) {
337 #if defined(BORINGSSL_FIPS)
338 static const bool is_fips = true;
339 #else
340 static const bool is_fips = false;
341 #endif
342
343 std::vector<Event> ret;
344 bool urandom_probed = false;
345 bool getrandom_ready = false;
346
347 // Probe for getrandom support
348 ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
349 std::function<void()> wait_for_entropy;
350 std::function<bool(bool, size_t)> sysrand;
351
352 if (flags & NO_GETRANDOM) {
353 ret.push_back(Event::Open("/dev/urandom"));
354 if (flags & NO_URANDOM) {
355 ret.push_back(Event::Abort());
356 return ret;
357 }
358
359 wait_for_entropy = [&ret, &urandom_probed, flags] {
360 if (!is_fips || urandom_probed) {
361 return;
362 }
363
364 // Probe urandom for entropy.
365 ret.push_back(Event::UrandomIoctl());
366 if (flags & URANDOM_NOT_READY) {
367 // If the first attempt doesn't report enough entropy, probe
368 // repeatedly until it does, which will happen with the second attempt.
369 ret.push_back(Event::UrandomIoctl());
370 }
371
372 urandom_probed = true;
373 };
374
375 sysrand = [&ret, &wait_for_entropy, flags](bool block, size_t len) {
376 if (block) {
377 wait_for_entropy();
378 }
379 ret.push_back(Event::UrandomRead(len));
380 if (flags & URANDOM_ERROR) {
381 ret.push_back(Event::Abort());
382 return false;
383 }
384 return true;
385 };
386 } else {
387 if (flags & GETRANDOM_ERROR) {
388 ret.push_back(Event::Abort());
389 return ret;
390 }
391
392 getrandom_ready = (flags & GETRANDOM_NOT_READY) == 0;
393 wait_for_entropy = [&ret, &getrandom_ready] {
394 if (getrandom_ready) {
395 return;
396 }
397
398 ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
399 ret.push_back(Event::GetRandom(1, 0));
400 getrandom_ready = true;
401 };
402 sysrand = [&ret, &wait_for_entropy](bool block, size_t len) {
403 if (block) {
404 wait_for_entropy();
405 }
406 ret.push_back(Event::GetRandom(len, block ? 0 : GRND_NONBLOCK));
407 return true;
408 };
409 }
410
411 const size_t kSeedLength = CTR_DRBG_ENTROPY_LEN * (is_fips ? 10 : 1);
412 const size_t kAdditionalDataLength = 32;
413
414 if (!have_rdrand()) {
415 if (!sysrand(true, kAdditionalDataLength) ||
416 // Initialise CRNGT.
417 (is_fips && !sysrand(true, 16)) ||
418 !sysrand(true, kSeedLength) ||
419 // Second entropy draw.
420 !sysrand(true, kAdditionalDataLength)) {
421 return ret;
422 }
423 } else {
424 // Opportuntistic entropy draw in FIPS mode because RDRAND was used.
425 // In non-FIPS mode it's just drawn from |CRYPTO_sysrand| in a blocking
426 // way.
427 if (!sysrand(!is_fips, CTR_DRBG_ENTROPY_LEN)) {
428 return ret;
429 }
430 }
431
432 return ret;
433 }
434
435 // Tests that |TestFunctionPRNGModel| is a correct model for the code in
436 // urandom.c, at least to the limits of the the |Event| type.
TEST(URandomTest,Test)437 TEST(URandomTest, Test) {
438 char buf[256];
439
440 #define TRACE_FLAG(flag) \
441 snprintf(buf, sizeof(buf), #flag ": %d", (flags & flag) != 0); \
442 SCOPED_TRACE(buf);
443
444 for (unsigned flags = 0; flags < NEXT_FLAG; flags++) {
445 TRACE_FLAG(NO_GETRANDOM);
446 TRACE_FLAG(NO_URANDOM);
447 TRACE_FLAG(GETRANDOM_NOT_READY);
448 TRACE_FLAG(URANDOM_NOT_READY);
449 TRACE_FLAG(GETRANDOM_ERROR);
450 TRACE_FLAG(URANDOM_ERROR);
451
452 const std::vector<Event> expected_trace = TestFunctionPRNGModel(flags);
453 std::vector<Event> actual_trace;
454 GetTrace(&actual_trace, flags, TestFunction);
455
456 if (expected_trace != actual_trace) {
457 ADD_FAILURE() << "Expected: " << ToString(expected_trace)
458 << "\nFound: " << ToString(actual_trace);
459 }
460 }
461 }
462
main(int argc,char ** argv)463 int main(int argc, char **argv) {
464 ::testing::InitGoogleTest(&argc, argv);
465 return RUN_ALL_TESTS();
466 }
467
468 #else
469
main(int argc,char ** argv)470 int main(int argc, char **argv) {
471 printf("PASS\n");
472 return 0;
473 }
474
475 #endif // X86_64 && LINUX && !SHARED_LIBRARY && !UNSAFE_DETERMINISTIC_MODE
476