blob: dcf9cc5603d099bdaf3d0bfd6856a0fc691f8ee0 [file] [log] [blame]
David Benjamin6f415952024-12-10 21:28:37 -05001/* Copyright 2019 The BoringSSL Authors
Adam Langley3e502c82019-10-16 09:56:38 -07002 *
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
David Benjamin9ff84912024-12-03 17:00:34 -050018#include <optional>
19
Adam Langley85a1e2e2022-07-29 18:12:37 +000020#include <openssl/bytestring.h>
Adam Langley24c01862022-05-06 16:14:47 -070021#include <openssl/ctrdrbg.h>
Adam Langley3e502c82019-10-16 09:56:38 -070022#include <openssl/rand.h>
23
Bob Beckec09a2d2024-04-29 23:15:36 +000024#include "../internal.h"
Adam Langley59fc5182024-12-16 11:02:22 -080025#include "getrandom_fillin.h"
Bob Beckec09a2d2024-04-29 23:15:36 +000026
Adam Langley3e502c82019-10-16 09:56:38 -070027
David Benjamina3692472023-06-22 21:03:19 -040028#if (defined(OPENSSL_X86_64) || defined(OPENSSL_AARCH64)) && \
29 !defined(BORINGSSL_SHARED_LIBRARY) && defined(OPENSSL_RAND_URANDOM) && \
30 defined(USE_NR_getrandom)
Adam Langley3e502c82019-10-16 09:56:38 -070031
Adam Langley85a1e2e2022-07-29 18:12:37 +000032#include <elf.h>
Adam Langley3e502c82019-10-16 09:56:38 -070033#include <linux/random.h>
34#include <sys/ptrace.h>
Adam Langley85a1e2e2022-07-29 18:12:37 +000035#include <sys/socket.h>
Adam Langley3e502c82019-10-16 09:56:38 -070036#include <sys/syscall.h>
Adam Langley85a1e2e2022-07-29 18:12:37 +000037#include <sys/uio.h>
38#include <sys/un.h>
Adam Langley3e502c82019-10-16 09:56:38 -070039#include <sys/user.h>
40
Adam Langley59fc5182024-12-16 11:02:22 -080041namespace {
42
Pete Bentley98f96942019-11-08 14:34:23 +000043#if !defined(PTRACE_O_EXITKILL)
44#define PTRACE_O_EXITKILL (1 << 20)
45#endif
46
Adam Langley85a1e2e2022-07-29 18:12:37 +000047#if defined(OPENSSL_ANDROID)
48static const bool kIsAndroid = true;
49#else
50static const bool kIsAndroid = false;
51#endif
52
53#if defined(BORINGSSL_FIPS)
54static const bool kIsFIPS = true;
55#else
56static const bool kIsFIPS = false;
57#endif
58
59static const bool kUsesDaemon = kIsFIPS && kIsAndroid;
60
61// kDaemonWriteLength is the number of bytes that the entropy daemon writes.
62static const size_t kDaemonWriteLength = 496;
63
Adam Langley3e502c82019-10-16 09:56:38 -070064// This test can be run with $OPENSSL_ia32cap=~0x4000000000000000 in order to
65// simulate the absence of RDRAND of machines that have it.
66
67// Event represents a system call from urandom.c that is observed by the ptrace
68// code in |GetTrace|.
69struct Event {
70 enum class Syscall {
71 kGetRandom,
72 kOpen,
73 kUrandomRead,
Adam Langley85a1e2e2022-07-29 18:12:37 +000074 kSocket,
75 kConnect,
76 kSocketRead,
77 kSocketClose,
Adam Langley3e502c82019-10-16 09:56:38 -070078 kAbort,
79 };
80
81 explicit Event(Syscall syscall) : type(syscall) {}
82
83 bool operator==(const Event &other) const {
Adam Langley59fc5182024-12-16 11:02:22 -080084 return type == other.type && //
85 length == other.length && //
86 flags == other.flags && //
Adam Langley85a1e2e2022-07-29 18:12:37 +000087 filename == other.filename;
Adam Langley3e502c82019-10-16 09:56:38 -070088 }
89
90 static Event GetRandom(size_t length, unsigned flags) {
91 Event e(Syscall::kGetRandom);
92 e.length = length;
93 e.flags = flags;
94 return e;
95 }
96
Adam Langley85a1e2e2022-07-29 18:12:37 +000097 static Event Open(const std::string &filename) {
Adam Langley3e502c82019-10-16 09:56:38 -070098 Event e(Syscall::kOpen);
99 e.filename = filename;
100 return e;
101 }
102
103 static Event UrandomRead(size_t length) {
104 Event e(Syscall::kUrandomRead);
105 e.length = length;
106 return e;
107 }
108
Adam Langley85a1e2e2022-07-29 18:12:37 +0000109 static Event Socket() {
110 Event e(Syscall::kSocket);
111 return e;
112 }
113
114 static Event Connect() {
115 Event e(Syscall::kConnect);
116 return e;
117 }
118
119 static Event SocketRead(size_t length) {
120 Event e(Syscall::kSocketRead);
121 e.length = length;
122 return e;
123 }
124
125 static Event SocketClose() {
126 Event e(Syscall::kSocketClose);
127 return e;
128 }
129
Adam Langley3e502c82019-10-16 09:56:38 -0700130 static Event Abort() {
131 Event e(Syscall::kAbort);
132 return e;
133 }
134
135 std::string String() const {
136 char buf[256];
137
138 switch (type) {
139 case Syscall::kGetRandom:
David Benjamin4f1fae32021-12-15 11:41:10 -0500140 snprintf(buf, sizeof(buf), "getrandom(_, %zu, %u)", length, flags);
Adam Langley3e502c82019-10-16 09:56:38 -0700141 break;
142
143 case Syscall::kOpen:
Adam Langley85a1e2e2022-07-29 18:12:37 +0000144 snprintf(buf, sizeof(buf), "open(%s, _)", filename.c_str());
Adam Langley3e502c82019-10-16 09:56:38 -0700145 break;
146
147 case Syscall::kUrandomRead:
148 snprintf(buf, sizeof(buf), "read(urandom_fd, _, %zu)", length);
149 break;
150
Adam Langley85a1e2e2022-07-29 18:12:37 +0000151 case Syscall::kSocket:
152 return "socket(UNIX, STREAM, _)";
153
154 case Syscall::kConnect:
155 return "connect(sock, _, _)";
156
157 case Syscall::kSocketRead:
158 snprintf(buf, sizeof(buf), "read(sock_fd, _, %zu)", length);
159 break;
160
161 case Syscall::kSocketClose:
162 return "close(sock)";
163
Adam Langley3e502c82019-10-16 09:56:38 -0700164 case Syscall::kAbort:
165 return "abort()";
166 }
167
168 return std::string(buf);
169 }
170
171 const Syscall type;
172 size_t length = 0;
173 unsigned flags = 0;
Adam Langley85a1e2e2022-07-29 18:12:37 +0000174 std::string filename;
Adam Langley3e502c82019-10-16 09:56:38 -0700175};
176
177static std::string ToString(const std::vector<Event> &trace) {
178 std::string ret;
179 for (const auto &event : trace) {
180 if (!ret.empty()) {
181 ret += ", ";
182 }
183 ret += event.String();
184 }
185 return ret;
186}
187
188// The following are flags to tell |GetTrace| to inject faults, using ptrace,
189// into the entropy-related system calls.
190
191// getrandom gives |ENOSYS|.
192static const unsigned NO_GETRANDOM = 1;
193// opening /dev/urandom fails.
194static const unsigned NO_URANDOM = 2;
195// getrandom always returns |EAGAIN| if given |GRNG_NONBLOCK|.
196static const unsigned GETRANDOM_NOT_READY = 4;
Adam Langley3e502c82019-10-16 09:56:38 -0700197// getrandom gives |EINVAL| unless |NO_GETRANDOM| is set.
David Benjamin19009c52022-08-29 13:05:06 -0400198static const unsigned GETRANDOM_ERROR = 8;
Adam Langley3e502c82019-10-16 09:56:38 -0700199// Reading from /dev/urandom gives |EINVAL|.
David Benjamin19009c52022-08-29 13:05:06 -0400200static const unsigned URANDOM_ERROR = 16;
Adam Langley85a1e2e2022-07-29 18:12:37 +0000201static const unsigned SOCKET_ERROR = 32;
202static const unsigned CONNECT_ERROR = 64;
203static const unsigned SOCKET_READ_ERROR = 128;
204static const unsigned SOCKET_READ_SHORT = 256;
205static const unsigned NEXT_FLAG = 512;
206
207// regs_read fetches the registers of |child_pid| and writes them to |out_regs|.
208// That structure will contain at least the following members:
209// syscall: the syscall number, if registers were read just before entering
210// one.
211// args[0..2]: syscall arguments, if registers were read just before
212// entering one.
213// ret: the syscall return value, if registers were read just after finishing
214// one.
215//
216// This call returns true on success and false otherwise.
217static bool regs_read(struct regs *out_regs, int child_pid);
218
219// regs_set_ret sets the return value of the system call that |child_pid| has
220// just finished, to |ret|. It returns true on success and false otherwise.
221static bool regs_set_ret(int child_pid, int ret);
222
223// regs_break_syscall causes the system call that |child_pid| is about to enter
224// to fail to run.
225static bool regs_break_syscall(int child_pid, const struct regs *orig_regs);
226
Adam Langley85a1e2e2022-07-29 18:12:37 +0000227struct regs {
228 uintptr_t syscall;
229 uintptr_t args[3];
230 uintptr_t ret;
231 struct user_regs_struct regs;
232};
233
David Benjamin8251dd82023-02-23 14:53:22 -0500234#if defined(OPENSSL_X86_64)
235
Adam Langley85a1e2e2022-07-29 18:12:37 +0000236static bool regs_read(struct regs *out_regs, int child_pid) {
237 if (ptrace(PTRACE_GETREGS, child_pid, nullptr, &out_regs->regs) != 0) {
238 return false;
239 }
240
241 out_regs->syscall = out_regs->regs.orig_rax;
242 out_regs->ret = out_regs->regs.rax;
243 out_regs->args[0] = out_regs->regs.rdi;
244 out_regs->args[1] = out_regs->regs.rsi;
245 out_regs->args[2] = out_regs->regs.rdx;
246 return true;
247}
248
249static bool regs_set_ret(int child_pid, int ret) {
250 struct regs regs;
251 if (!regs_read(&regs, child_pid)) {
252 return false;
253 }
254 regs.regs.rax = ret;
255 return ptrace(PTRACE_SETREGS, child_pid, nullptr, &regs.regs) == 0;
256}
257
258static bool regs_break_syscall(int child_pid, const struct regs *orig_regs) {
David Benjamin5eb93432023-02-23 15:50:29 -0500259 // Replace the syscall number with -1 to cause the kernel to fail the call.
260 struct user_regs_struct regs = orig_regs->regs;
261 regs.orig_rax = -1;
Adam Langley85a1e2e2022-07-29 18:12:37 +0000262 return ptrace(PTRACE_SETREGS, child_pid, nullptr, &regs) == 0;
263}
264
265#elif defined(OPENSSL_AARCH64)
266
Adam Langley85a1e2e2022-07-29 18:12:37 +0000267static bool regs_read(struct regs *out_regs, int child_pid) {
268 struct iovec io;
David Benjamin8251dd82023-02-23 14:53:22 -0500269 io.iov_base = &out_regs->regs;
Adam Langley85a1e2e2022-07-29 18:12:37 +0000270 io.iov_len = sizeof(out_regs->regs);
271 if (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &io) != 0) {
272 return false;
273 }
274
David Benjamin8251dd82023-02-23 14:53:22 -0500275 out_regs->syscall = out_regs->regs.regs[8];
276 out_regs->ret = out_regs->regs.regs[0];
277 out_regs->args[0] = out_regs->regs.regs[0];
278 out_regs->args[1] = out_regs->regs.regs[1];
279 out_regs->args[2] = out_regs->regs.regs[2];
Adam Langley85a1e2e2022-07-29 18:12:37 +0000280
281 return true;
282}
283
David Benjamin5eb93432023-02-23 15:50:29 -0500284static bool set_regset(int child_pid, int regset, const void *data,
285 size_t len) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000286 struct iovec io;
David Benjamin5eb93432023-02-23 15:50:29 -0500287 io.iov_base = const_cast<void *>(data);
288 io.iov_len = len;
289 return ptrace(PTRACE_SETREGSET, child_pid, reinterpret_cast<void *>(regset),
290 &io) == 0;
Adam Langley85a1e2e2022-07-29 18:12:37 +0000291}
292
293static bool regs_set_ret(int child_pid, int ret) {
294 struct regs regs;
295 if (!regs_read(&regs, child_pid)) {
296 return false;
297 }
David Benjamin8251dd82023-02-23 14:53:22 -0500298 regs.regs.regs[0] = ret;
David Benjamin5eb93432023-02-23 15:50:29 -0500299 return set_regset(child_pid, NT_PRSTATUS, &regs.regs, sizeof(regs.regs));
Adam Langley85a1e2e2022-07-29 18:12:37 +0000300}
301
302static bool regs_break_syscall(int child_pid, const struct regs *orig_regs) {
David Benjamin5eb93432023-02-23 15:50:29 -0500303 // Replace the syscall number with -1 to cause the kernel to fail the call.
304 int syscall = -1;
305 return set_regset(child_pid, NT_ARM_SYSTEM_CALL, &syscall, sizeof(syscall));
Adam Langley85a1e2e2022-07-29 18:12:37 +0000306}
307
308#endif
309
Adam Langley85a1e2e2022-07-29 18:12:37 +0000310// memcpy_to_remote copies |n| bytes from |in_src| in the local address space,
311// to |dest| in the address space of |child_pid|.
312static void memcpy_to_remote(int child_pid, uint64_t dest, const void *in_src,
313 size_t n) {
314 const uint8_t *src = reinterpret_cast<const uint8_t *>(in_src);
315
316 // ptrace always works with ill-defined "words", which appear to be 64-bit
317 // on 64-bit systems.
318#if !defined(OPENSSL_64_BIT)
319#error "This code probably doesn't work"
320#endif
321
322 while (n) {
323 const uintptr_t aligned_addr = dest & ~7;
324 const uintptr_t offset = dest - aligned_addr;
325 const size_t space = 8 - offset;
326 size_t todo = n;
327 if (todo > space) {
328 todo = space;
329 }
330
331 uint64_t word;
332 if (offset == 0 && todo == 8) {
333 word = CRYPTO_load_u64_le(src);
334 } else {
335 uint8_t bytes[8];
336 CRYPTO_store_u64_le(
337 bytes, ptrace(PTRACE_PEEKDATA, child_pid,
338 reinterpret_cast<void *>(aligned_addr), nullptr));
339 memcpy(&bytes[offset], src, todo);
340 word = CRYPTO_load_u64_le(bytes);
341 }
342
343 ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, child_pid,
344 reinterpret_cast<void *>(aligned_addr),
345 reinterpret_cast<void *>(word)));
346
347 src += todo;
348 n -= todo;
349 dest += todo;
350 }
351}
352
353static uint8_t get_byte_from_remote(int child_pid, uint64_t ptr) {
354 // ptrace always works with ill-defined "words", which appear to be 64-bit
355 // on 64-bit systems.
356#if !defined(OPENSSL_64_BIT)
357#error "This code probably doesn't work"
358#endif
359
360 const uintptr_t aligned_addr = ptr & ~7;
361 const uintptr_t offset = ptr - aligned_addr;
362
363 uint64_t word = ptrace(PTRACE_PEEKDATA, child_pid,
364 reinterpret_cast<void *>(aligned_addr), 0);
365 uint8_t bytes[8];
366 CRYPTO_store_u64_le(bytes, word);
367 return bytes[offset];
368}
369
370static std::string get_string_from_remote(int child_pid, uint64_t ptr) {
371 std::string ret;
372
373 for (;;) {
374 const uint8_t byte = get_byte_from_remote(child_pid, ptr);
375 if (byte == 0) {
376 break;
377 }
378 ret.push_back((char)byte);
379 ptr++;
380 }
381
382 return ret;
383}
Adam Langley3e502c82019-10-16 09:56:38 -0700384
385// GetTrace runs |thunk| in a forked process and observes the resulting system
386// calls using ptrace. It simulates a variety of failures based on the contents
387// of |flags| and records the observed events by appending to |out_trace|.
388static void GetTrace(std::vector<Event> *out_trace, unsigned flags,
389 std::function<void()> thunk) {
390 const int child_pid = fork();
391 ASSERT_NE(-1, child_pid);
392
393 if (child_pid == 0) {
394 // Child process
395 if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) {
396 perror("PTRACE_TRACEME");
397 _exit(1);
398 }
399 raise(SIGSTOP);
400 thunk();
401 _exit(0);
402 }
403
404 // Parent process
405 int status;
406 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
David Benjaminbffae8a2023-02-23 12:51:29 -0500407 ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP)
408 << "Child was not stopped with SIGSTOP: " << status;
Adam Langley3e502c82019-10-16 09:56:38 -0700409
410 // Set options so that:
411 // a) the child process is killed once this process dies.
412 // b) System calls result in a WSTOPSIG value of (SIGTRAP | 0x80) rather
413 // than just SIGTRAP. (This doesn't matter here, but it's recommended
414 // practice so that it's distinct from the signal itself.)
415 ASSERT_EQ(0, ptrace(PTRACE_SETOPTIONS, child_pid, nullptr,
416 PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD))
417 << strerror(errno);
418
419 // urandom_fd tracks the file descriptor number for /dev/urandom in the child
420 // process, if it opens it.
421 int urandom_fd = -1;
422
Adam Langley85a1e2e2022-07-29 18:12:37 +0000423 // sock_fd tracks the file descriptor number for the socket to the entropy
424 // daemon, if one is opened.
425 int sock_fd = -1;
426
Adam Langley3e502c82019-10-16 09:56:38 -0700427 for (;;) {
428 // Advance the child to the next system call.
429 ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0));
430 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
431
432 // The child may have aborted rather than made a system call.
433 if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGABRT) {
434 out_trace->push_back(Event::Abort());
435 break;
436 }
437
438 // Otherwise the only valid ptrace event is a system call stop.
David Benjaminbffae8a2023-02-23 12:51:29 -0500439 ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80))
440 << "Child was not stopped with a syscall stop: " << status;
Adam Langley3e502c82019-10-16 09:56:38 -0700441
Adam Langley85a1e2e2022-07-29 18:12:37 +0000442 struct regs regs;
443 ASSERT_TRUE(regs_read(&regs, child_pid));
Adam Langley3e502c82019-10-16 09:56:38 -0700444
445 bool is_opening_urandom = false;
Adam Langley85a1e2e2022-07-29 18:12:37 +0000446 bool is_socket_call = false;
447 bool is_socket_read = false;
448 uint64_t socket_read_bytes = 0;
449 // force_result is unset to indicate that the system call should run
Adam Langley3e502c82019-10-16 09:56:38 -0700450 // normally. Otherwise it's, e.g. -EINVAL, to indicate that the system call
Adam Langley85a1e2e2022-07-29 18:12:37 +0000451 // should not run and that the given value should be injected on return.
David Benjamin9ff84912024-12-03 17:00:34 -0500452 std::optional<int> force_result;
Adam Langley3e502c82019-10-16 09:56:38 -0700453
Adam Langley85a1e2e2022-07-29 18:12:37 +0000454 switch (regs.syscall) {
Adam Langley3e502c82019-10-16 09:56:38 -0700455 case __NR_getrandom:
456 if (flags & NO_GETRANDOM) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000457 force_result = -ENOSYS;
Adam Langley3e502c82019-10-16 09:56:38 -0700458 } else if (flags & GETRANDOM_ERROR) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000459 force_result = -EINVAL;
Adam Langley3e502c82019-10-16 09:56:38 -0700460 } else if (flags & GETRANDOM_NOT_READY) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000461 if (regs.args[2] & GRND_NONBLOCK) {
462 force_result = -EAGAIN;
Adam Langley3e502c82019-10-16 09:56:38 -0700463 }
464 }
465 out_trace->push_back(
Adam Langley85a1e2e2022-07-29 18:12:37 +0000466 Event::GetRandom(/*length=*/regs.args[1], /*flags=*/regs.args[2]));
Adam Langley3e502c82019-10-16 09:56:38 -0700467 break;
468
469 case __NR_openat:
Adam Langley85a1e2e2022-07-29 18:12:37 +0000470#if defined(OPENSSL_X86_64)
471 case __NR_open:
472#endif
473 {
474 uintptr_t filename_ptr =
475 (regs.syscall == __NR_openat) ? regs.args[1] : regs.args[0];
Adam Langley59fc5182024-12-16 11:02:22 -0800476 const std::string filename =
477 get_string_from_remote(child_pid, filename_ptr);
Adam Langley85a1e2e2022-07-29 18:12:37 +0000478 if (filename.find("/dev/__properties__/") == 0) {
479 // Android may try opening these files as part of SELinux support.
480 // They are ignored here.
481 } else {
482 out_trace->push_back(Event::Open(filename));
483 }
484 is_opening_urandom = (filename == "/dev/urandom");
Adam Langley3e502c82019-10-16 09:56:38 -0700485 if (is_opening_urandom && (flags & NO_URANDOM)) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000486 force_result = -ENOENT;
Adam Langley3e502c82019-10-16 09:56:38 -0700487 }
488 break;
489 }
490
491 case __NR_read: {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000492 const int read_fd = regs.args[0];
Adam Langley3e502c82019-10-16 09:56:38 -0700493 if (urandom_fd >= 0 && urandom_fd == read_fd) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000494 out_trace->push_back(Event::UrandomRead(/*length=*/regs.args[2]));
Adam Langley3e502c82019-10-16 09:56:38 -0700495 if (flags & URANDOM_ERROR) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000496 force_result = -EINVAL;
497 }
498 } else if (sock_fd >= 0 && sock_fd == read_fd) {
499 uint64_t length = regs.args[2];
500 out_trace->push_back(Event::SocketRead(length));
501 if (flags & SOCKET_READ_ERROR) {
502 force_result = -EINVAL;
503 } else {
504 is_socket_read = true;
505 socket_read_bytes = length;
506
507 if (flags & SOCKET_READ_SHORT) {
508 ASSERT_GT(socket_read_bytes, 0u);
509 socket_read_bytes--;
510 flags &= ~SOCKET_READ_SHORT;
511 }
Adam Langley4259ae82022-05-10 11:44:16 -0700512 }
Adam Langley4259ae82022-05-10 11:44:16 -0700513 }
514 break;
515 }
Adam Langley85a1e2e2022-07-29 18:12:37 +0000516
517 case __NR_close: {
518 if (sock_fd >= 0 && static_cast<int>(regs.args[0]) == sock_fd) {
519 out_trace->push_back(Event::SocketClose());
520 sock_fd = -1;
521 }
522 break;
523 }
524
525 case __NR_socket: {
526 const int family = regs.args[0];
527 const int type = regs.args[1];
528 if (family == AF_UNIX && type == SOCK_STREAM) {
529 out_trace->push_back(Event::Socket());
530 is_socket_call = true;
531 if (flags & SOCKET_ERROR) {
532 force_result = -EINVAL;
533 }
534 }
535 break;
536 }
537
538 case __NR_connect: {
539 const int connect_fd = regs.args[0];
540 if (sock_fd >= 0 && connect_fd == sock_fd) {
541 out_trace->push_back(Event::Connect());
542 if (flags & CONNECT_ERROR) {
543 force_result = -EINVAL;
544 } else {
545 // The test system might not have an entropy daemon running so
546 // inject a success result.
547 force_result = 0;
548 }
549 }
550
551 break;
552 }
Adam Langley3e502c82019-10-16 09:56:38 -0700553 }
554
Adam Langley85a1e2e2022-07-29 18:12:37 +0000555 if (force_result.has_value()) {
556 ASSERT_TRUE(regs_break_syscall(child_pid, &regs));
Adam Langley3e502c82019-10-16 09:56:38 -0700557 }
558
559 ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0));
560 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
561 // If the system call was exit/exit_group, the process may be terminated
562 // rather than have exited the system call.
563 if (WIFEXITED(status)) {
564 ASSERT_EQ(0, WEXITSTATUS(status));
565 return;
566 }
567
568 // Otherwise the next state must be a system call exit stop. This is
569 // indistinguishable from a system call entry, we just have to keep track
570 // and know that these events happen in pairs.
571 ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80));
572
Adam Langley85a1e2e2022-07-29 18:12:37 +0000573 if (force_result.has_value()) {
574 ASSERT_TRUE(regs_set_ret(child_pid, force_result.value()));
Adam Langley3e502c82019-10-16 09:56:38 -0700575 } else if (is_opening_urandom) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000576 ASSERT_TRUE(regs_read(&regs, child_pid));
577 urandom_fd = regs.ret;
578 } else if (is_socket_call) {
579 ASSERT_TRUE(regs_read(&regs, child_pid));
580 sock_fd = regs.ret;
581 } else if (is_socket_read) {
582 // Simulate a response from the entropy daemon since it might not be
583 // running on the current system.
584 uint8_t entropy[kDaemonWriteLength];
585 ASSERT_LE(socket_read_bytes, sizeof(entropy));
586
587 for (size_t i = 0; i < sizeof(entropy); i++) {
588 entropy[i] = i & 0xff;
589 }
590 memcpy_to_remote(child_pid, regs.args[1], entropy, socket_read_bytes);
591
592 ASSERT_TRUE(regs_set_ret(child_pid, socket_read_bytes));
Adam Langley3e502c82019-10-16 09:56:38 -0700593 }
594 }
595}
596
597// TestFunction is the function that |GetTrace| is asked to trace.
598static void TestFunction() {
599 uint8_t byte;
600 RAND_bytes(&byte, sizeof(byte));
601 RAND_bytes(&byte, sizeof(byte));
602}
603
Adam Langley85a1e2e2022-07-29 18:12:37 +0000604static bool have_fork_detection() { return CRYPTO_get_fork_generation() != 0; }
605
606static bool AppendDaemonEvents(std::vector<Event> *events, unsigned flags) {
607 events->push_back(Event::Socket());
608 if (flags & SOCKET_ERROR) {
609 return false;
610 }
611
612 bool ret = false;
613 events->push_back(Event::Connect());
614 if (flags & CONNECT_ERROR) {
615 goto out;
616 }
617
618 events->push_back(Event::SocketRead(kDaemonWriteLength));
619 if (flags & SOCKET_READ_ERROR) {
620 goto out;
621 }
622
623 if (flags & SOCKET_READ_SHORT) {
624 events->push_back(Event::SocketRead(1));
625 }
626
627 ret = true;
628
629out:
630 events->push_back(Event::SocketClose());
631 return ret;
David Benjaminb1086cd2019-02-01 00:45:23 +0000632}
633
Adam Langley3e502c82019-10-16 09:56:38 -0700634// TestFunctionPRNGModel is a model of how the urandom.c code will behave when
635// |TestFunction| is run. It should return the same trace of events that
636// |GetTrace| will observe the real code making.
637static std::vector<Event> TestFunctionPRNGModel(unsigned flags) {
Adam Langley3e502c82019-10-16 09:56:38 -0700638 std::vector<Event> ret;
Adam Langley3e502c82019-10-16 09:56:38 -0700639 bool getrandom_ready = false;
Adam Langley76cb7c52023-03-01 00:09:55 +0000640 bool used_daemon = false;
641
642 if (have_fork_detection()) {
643 used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags);
644 }
Adam Langley3e502c82019-10-16 09:56:38 -0700645
646 // Probe for getrandom support
647 ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
648 std::function<void()> wait_for_entropy;
649 std::function<bool(bool, size_t)> sysrand;
650
651 if (flags & NO_GETRANDOM) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000652 if (kIsFIPS) {
David Benjamin19009c52022-08-29 13:05:06 -0400653 // FIPS builds require getrandom.
654 ret.push_back(Event::Abort());
655 return ret;
656 }
657
Adam Langley3e502c82019-10-16 09:56:38 -0700658 ret.push_back(Event::Open("/dev/urandom"));
659 if (flags & NO_URANDOM) {
660 ret.push_back(Event::Abort());
661 return ret;
662 }
663
David Benjamin19009c52022-08-29 13:05:06 -0400664 sysrand = [&ret, flags](bool block, size_t len) {
Adam Langley3e502c82019-10-16 09:56:38 -0700665 ret.push_back(Event::UrandomRead(len));
666 if (flags & URANDOM_ERROR) {
667 ret.push_back(Event::Abort());
668 return false;
669 }
670 return true;
671 };
672 } else {
673 if (flags & GETRANDOM_ERROR) {
674 ret.push_back(Event::Abort());
675 return ret;
676 }
677
678 getrandom_ready = (flags & GETRANDOM_NOT_READY) == 0;
679 wait_for_entropy = [&ret, &getrandom_ready] {
680 if (getrandom_ready) {
681 return;
682 }
683
684 ret.push_back(Event::GetRandom(1, GRND_NONBLOCK));
685 ret.push_back(Event::GetRandom(1, 0));
686 getrandom_ready = true;
687 };
688 sysrand = [&ret, &wait_for_entropy](bool block, size_t len) {
689 if (block) {
690 wait_for_entropy();
691 }
692 ret.push_back(Event::GetRandom(len, block ? 0 : GRND_NONBLOCK));
693 return true;
694 };
695 }
696
Adam Langley85a1e2e2022-07-29 18:12:37 +0000697 const size_t kSeedLength = CTR_DRBG_ENTROPY_LEN * (kIsFIPS ? 10 : 1);
Adam Langley3e502c82019-10-16 09:56:38 -0700698 const size_t kAdditionalDataLength = 32;
699
700 if (!have_rdrand()) {
Adam Langley76cb7c52023-03-01 00:09:55 +0000701 if (!have_fork_detection()) {
702 if (!sysrand(true, kAdditionalDataLength)) {
703 return ret;
704 }
705 used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags);
706 }
Adam Langley59fc5182024-12-16 11:02:22 -0800707 if ( // Initialise CRNGT.
Adam Langley85a1e2e2022-07-29 18:12:37 +0000708 (!used_daemon && !sysrand(true, kSeedLength + (kIsFIPS ? 16 : 0))) ||
709 // Personalisation draw if the daemon was used.
710 (used_daemon && !sysrand(false, CTR_DRBG_ENTROPY_LEN)) ||
Adam Langley3e502c82019-10-16 09:56:38 -0700711 // Second entropy draw.
David Benjaminb1086cd2019-02-01 00:45:23 +0000712 (!have_fork_detection() && !sysrand(true, kAdditionalDataLength))) {
Adam Langley3e502c82019-10-16 09:56:38 -0700713 return ret;
714 }
Adam Langley7a22a652020-04-07 10:53:24 -0700715 } else if (
716 // First additional data. If fast RDRAND isn't available then a
717 // non-blocking OS entropy draw will be tried.
David Benjaminb1086cd2019-02-01 00:45:23 +0000718 (!have_fast_rdrand() && !have_fork_detection() &&
719 !sysrand(false, kAdditionalDataLength)) ||
Adam Langley7a22a652020-04-07 10:53:24 -0700720 // Opportuntistic entropy draw in FIPS mode because RDRAND was used.
721 // In non-FIPS mode it's just drawn from |CRYPTO_sysrand| in a blocking
722 // way.
Adam Langley85a1e2e2022-07-29 18:12:37 +0000723 !sysrand(!kIsFIPS, CTR_DRBG_ENTROPY_LEN) ||
Adam Langley7a22a652020-04-07 10:53:24 -0700724 // Second entropy draw's additional data.
David Benjaminb1086cd2019-02-01 00:45:23 +0000725 (!have_fast_rdrand() && !have_fork_detection() &&
726 !sysrand(false, kAdditionalDataLength))) {
Adam Langley7a22a652020-04-07 10:53:24 -0700727 return ret;
Adam Langley3e502c82019-10-16 09:56:38 -0700728 }
729
730 return ret;
731}
732
Adam Langley7a22a652020-04-07 10:53:24 -0700733static void CheckInvariants(const std::vector<Event> &events) {
734 // If RDRAND is available then there should be no blocking syscalls in FIPS
735 // mode.
736#if defined(BORINGSSL_FIPS)
737 if (have_rdrand()) {
738 for (const auto &event : events) {
739 switch (event.type) {
740 case Event::Syscall::kGetRandom:
741 if ((event.flags & GRND_NONBLOCK) == 0) {
742 ADD_FAILURE() << "Blocking getrandom found with RDRAND: "
743 << ToString(events);
744 }
745 break;
746
Adam Langley7a22a652020-04-07 10:53:24 -0700747 default:
748 break;
749 }
750 }
751 }
752#endif
753}
754
Adam Langley3e502c82019-10-16 09:56:38 -0700755// Tests that |TestFunctionPRNGModel| is a correct model for the code in
756// urandom.c, at least to the limits of the the |Event| type.
757TEST(URandomTest, Test) {
758 char buf[256];
759
Adam Langley3cd7faa2023-02-23 00:23:27 +0000760 // Some Android systems lack getrandom.
761 uint8_t scratch[1];
762 const bool has_getrandom =
763 (syscall(__NR_getrandom, scratch, sizeof(scratch), GRND_NONBLOCK) != -1 ||
764 errno != ENOSYS);
765
Adam Langley3e502c82019-10-16 09:56:38 -0700766#define TRACE_FLAG(flag) \
767 snprintf(buf, sizeof(buf), #flag ": %d", (flags & flag) != 0); \
768 SCOPED_TRACE(buf);
769
770 for (unsigned flags = 0; flags < NEXT_FLAG; flags++) {
Adam Langley85a1e2e2022-07-29 18:12:37 +0000771 if (!kUsesDaemon && (flags & (SOCKET_ERROR | CONNECT_ERROR |
772 SOCKET_READ_ERROR | SOCKET_READ_SHORT))) {
773 // These cases are meaningless unless the code will try to use the entropy
774 // daemon.
775 continue;
776 }
777
Adam Langley3cd7faa2023-02-23 00:23:27 +0000778 if (!has_getrandom && !(flags & NO_GETRANDOM)) {
Adam Langley59fc5182024-12-16 11:02:22 -0800779 continue;
Adam Langley3cd7faa2023-02-23 00:23:27 +0000780 }
781
Adam Langley3e502c82019-10-16 09:56:38 -0700782 TRACE_FLAG(NO_GETRANDOM);
783 TRACE_FLAG(NO_URANDOM);
784 TRACE_FLAG(GETRANDOM_NOT_READY);
Adam Langley3e502c82019-10-16 09:56:38 -0700785 TRACE_FLAG(GETRANDOM_ERROR);
786 TRACE_FLAG(URANDOM_ERROR);
Adam Langley85a1e2e2022-07-29 18:12:37 +0000787 TRACE_FLAG(SOCKET_ERROR);
788 TRACE_FLAG(CONNECT_ERROR);
789 TRACE_FLAG(SOCKET_READ_ERROR);
790 TRACE_FLAG(SOCKET_READ_SHORT);
Adam Langley3e502c82019-10-16 09:56:38 -0700791
792 const std::vector<Event> expected_trace = TestFunctionPRNGModel(flags);
Adam Langley7a22a652020-04-07 10:53:24 -0700793 CheckInvariants(expected_trace);
Adam Langley3e502c82019-10-16 09:56:38 -0700794 std::vector<Event> actual_trace;
795 GetTrace(&actual_trace, flags, TestFunction);
796
797 if (expected_trace != actual_trace) {
798 ADD_FAILURE() << "Expected: " << ToString(expected_trace)
799 << "\nFound: " << ToString(actual_trace);
800 }
801 }
802}
803
Adam Langley59fc5182024-12-16 11:02:22 -0800804} // namespace
805
Adam Langley3e502c82019-10-16 09:56:38 -0700806int main(int argc, char **argv) {
807 ::testing::InitGoogleTest(&argc, argv);
David Benjaminb1086cd2019-02-01 00:45:23 +0000808
809 if (getenv("BORINGSSL_IGNORE_MADV_WIPEONFORK")) {
Adam Langley76cb7c52023-03-01 00:09:55 +0000810 CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(0);
811 } else {
812 CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(1);
David Benjaminb1086cd2019-02-01 00:45:23 +0000813 }
814
Adam Langley3e502c82019-10-16 09:56:38 -0700815 return RUN_ALL_TESTS();
816}
817
818#else
819
Adam Langley7de94982019-10-17 09:15:37 -0700820int main(int argc, char **argv) {
821 printf("PASS\n");
822 return 0;
823}
Adam Langley3e502c82019-10-16 09:56:38 -0700824
Adam Langley85a1e2e2022-07-29 18:12:37 +0000825#endif // (X86_64 || AARCH64) && !SHARED_LIBRARY &&
826 // !UNSAFE_DETERMINISTIC_MODE && USE_NR_getrandom