David Benjamin | 6f41595 | 2024-12-10 21:28:37 -0500 | [diff] [blame] | 1 | /* Copyright 2019 The BoringSSL Authors |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 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 | |
David Benjamin | 9ff8491 | 2024-12-03 17:00:34 -0500 | [diff] [blame] | 18 | #include <optional> |
| 19 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 20 | #include <openssl/bytestring.h> |
Adam Langley | 24c0186 | 2022-05-06 16:14:47 -0700 | [diff] [blame] | 21 | #include <openssl/ctrdrbg.h> |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 22 | #include <openssl/rand.h> |
| 23 | |
Bob Beck | ec09a2d | 2024-04-29 23:15:36 +0000 | [diff] [blame] | 24 | #include "../internal.h" |
Adam Langley | 59fc518 | 2024-12-16 11:02:22 -0800 | [diff] [blame] | 25 | #include "getrandom_fillin.h" |
Bob Beck | ec09a2d | 2024-04-29 23:15:36 +0000 | [diff] [blame] | 26 | |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 27 | |
David Benjamin | a369247 | 2023-06-22 21:03:19 -0400 | [diff] [blame] | 28 | #if (defined(OPENSSL_X86_64) || defined(OPENSSL_AARCH64)) && \ |
| 29 | !defined(BORINGSSL_SHARED_LIBRARY) && defined(OPENSSL_RAND_URANDOM) && \ |
| 30 | defined(USE_NR_getrandom) |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 31 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 32 | #include <elf.h> |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 33 | #include <linux/random.h> |
| 34 | #include <sys/ptrace.h> |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 35 | #include <sys/socket.h> |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 36 | #include <sys/syscall.h> |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 37 | #include <sys/uio.h> |
| 38 | #include <sys/un.h> |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 39 | #include <sys/user.h> |
| 40 | |
Adam Langley | 59fc518 | 2024-12-16 11:02:22 -0800 | [diff] [blame] | 41 | namespace { |
| 42 | |
Pete Bentley | 98f9694 | 2019-11-08 14:34:23 +0000 | [diff] [blame] | 43 | #if !defined(PTRACE_O_EXITKILL) |
| 44 | #define PTRACE_O_EXITKILL (1 << 20) |
| 45 | #endif |
| 46 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 47 | #if defined(OPENSSL_ANDROID) |
| 48 | static const bool kIsAndroid = true; |
| 49 | #else |
| 50 | static const bool kIsAndroid = false; |
| 51 | #endif |
| 52 | |
| 53 | #if defined(BORINGSSL_FIPS) |
| 54 | static const bool kIsFIPS = true; |
| 55 | #else |
| 56 | static const bool kIsFIPS = false; |
| 57 | #endif |
| 58 | |
| 59 | static const bool kUsesDaemon = kIsFIPS && kIsAndroid; |
| 60 | |
| 61 | // kDaemonWriteLength is the number of bytes that the entropy daemon writes. |
| 62 | static const size_t kDaemonWriteLength = 496; |
| 63 | |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 64 | // 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|. |
| 69 | struct Event { |
| 70 | enum class Syscall { |
| 71 | kGetRandom, |
| 72 | kOpen, |
| 73 | kUrandomRead, |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 74 | kSocket, |
| 75 | kConnect, |
| 76 | kSocketRead, |
| 77 | kSocketClose, |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 78 | kAbort, |
| 79 | }; |
| 80 | |
| 81 | explicit Event(Syscall syscall) : type(syscall) {} |
| 82 | |
| 83 | bool operator==(const Event &other) const { |
Adam Langley | 59fc518 | 2024-12-16 11:02:22 -0800 | [diff] [blame] | 84 | return type == other.type && // |
| 85 | length == other.length && // |
| 86 | flags == other.flags && // |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 87 | filename == other.filename; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 88 | } |
| 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 97 | static Event Open(const std::string &filename) { |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 98 | 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 109 | 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 130 | 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 Benjamin | 4f1fae3 | 2021-12-15 11:41:10 -0500 | [diff] [blame] | 140 | snprintf(buf, sizeof(buf), "getrandom(_, %zu, %u)", length, flags); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 141 | break; |
| 142 | |
| 143 | case Syscall::kOpen: |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 144 | snprintf(buf, sizeof(buf), "open(%s, _)", filename.c_str()); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 145 | break; |
| 146 | |
| 147 | case Syscall::kUrandomRead: |
| 148 | snprintf(buf, sizeof(buf), "read(urandom_fd, _, %zu)", length); |
| 149 | break; |
| 150 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 151 | 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 164 | 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 174 | std::string filename; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 175 | }; |
| 176 | |
| 177 | static 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|. |
| 192 | static const unsigned NO_GETRANDOM = 1; |
| 193 | // opening /dev/urandom fails. |
| 194 | static const unsigned NO_URANDOM = 2; |
| 195 | // getrandom always returns |EAGAIN| if given |GRNG_NONBLOCK|. |
| 196 | static const unsigned GETRANDOM_NOT_READY = 4; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 197 | // getrandom gives |EINVAL| unless |NO_GETRANDOM| is set. |
David Benjamin | 19009c5 | 2022-08-29 13:05:06 -0400 | [diff] [blame] | 198 | static const unsigned GETRANDOM_ERROR = 8; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 199 | // Reading from /dev/urandom gives |EINVAL|. |
David Benjamin | 19009c5 | 2022-08-29 13:05:06 -0400 | [diff] [blame] | 200 | static const unsigned URANDOM_ERROR = 16; |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 201 | static const unsigned SOCKET_ERROR = 32; |
| 202 | static const unsigned CONNECT_ERROR = 64; |
| 203 | static const unsigned SOCKET_READ_ERROR = 128; |
| 204 | static const unsigned SOCKET_READ_SHORT = 256; |
| 205 | static 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. |
| 217 | static 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. |
| 221 | static 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. |
| 225 | static bool regs_break_syscall(int child_pid, const struct regs *orig_regs); |
| 226 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 227 | struct regs { |
| 228 | uintptr_t syscall; |
| 229 | uintptr_t args[3]; |
| 230 | uintptr_t ret; |
| 231 | struct user_regs_struct regs; |
| 232 | }; |
| 233 | |
David Benjamin | 8251dd8 | 2023-02-23 14:53:22 -0500 | [diff] [blame] | 234 | #if defined(OPENSSL_X86_64) |
| 235 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 236 | static 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 | |
| 249 | static bool regs_set_ret(int child_pid, int ret) { |
| 250 | struct regs regs; |
| 251 | if (!regs_read(®s, child_pid)) { |
| 252 | return false; |
| 253 | } |
| 254 | regs.regs.rax = ret; |
| 255 | return ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s.regs) == 0; |
| 256 | } |
| 257 | |
| 258 | static bool regs_break_syscall(int child_pid, const struct regs *orig_regs) { |
David Benjamin | 5eb9343 | 2023-02-23 15:50:29 -0500 | [diff] [blame] | 259 | // 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 262 | return ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s) == 0; |
| 263 | } |
| 264 | |
| 265 | #elif defined(OPENSSL_AARCH64) |
| 266 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 267 | static bool regs_read(struct regs *out_regs, int child_pid) { |
| 268 | struct iovec io; |
David Benjamin | 8251dd8 | 2023-02-23 14:53:22 -0500 | [diff] [blame] | 269 | io.iov_base = &out_regs->regs; |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 270 | io.iov_len = sizeof(out_regs->regs); |
| 271 | if (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &io) != 0) { |
| 272 | return false; |
| 273 | } |
| 274 | |
David Benjamin | 8251dd8 | 2023-02-23 14:53:22 -0500 | [diff] [blame] | 275 | 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 280 | |
| 281 | return true; |
| 282 | } |
| 283 | |
David Benjamin | 5eb9343 | 2023-02-23 15:50:29 -0500 | [diff] [blame] | 284 | static bool set_regset(int child_pid, int regset, const void *data, |
| 285 | size_t len) { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 286 | struct iovec io; |
David Benjamin | 5eb9343 | 2023-02-23 15:50:29 -0500 | [diff] [blame] | 287 | 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 291 | } |
| 292 | |
| 293 | static bool regs_set_ret(int child_pid, int ret) { |
| 294 | struct regs regs; |
| 295 | if (!regs_read(®s, child_pid)) { |
| 296 | return false; |
| 297 | } |
David Benjamin | 8251dd8 | 2023-02-23 14:53:22 -0500 | [diff] [blame] | 298 | regs.regs.regs[0] = ret; |
David Benjamin | 5eb9343 | 2023-02-23 15:50:29 -0500 | [diff] [blame] | 299 | return set_regset(child_pid, NT_PRSTATUS, ®s.regs, sizeof(regs.regs)); |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 300 | } |
| 301 | |
| 302 | static bool regs_break_syscall(int child_pid, const struct regs *orig_regs) { |
David Benjamin | 5eb9343 | 2023-02-23 15:50:29 -0500 | [diff] [blame] | 303 | // 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 306 | } |
| 307 | |
| 308 | #endif |
| 309 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 310 | // memcpy_to_remote copies |n| bytes from |in_src| in the local address space, |
| 311 | // to |dest| in the address space of |child_pid|. |
| 312 | static 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 | |
| 353 | static 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 | |
| 370 | static 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 384 | |
| 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|. |
| 388 | static 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 Benjamin | bffae8a | 2023-02-23 12:51:29 -0500 | [diff] [blame] | 407 | ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) |
| 408 | << "Child was not stopped with SIGSTOP: " << status; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 409 | |
| 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 423 | // 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 427 | 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 Benjamin | bffae8a | 2023-02-23 12:51:29 -0500 | [diff] [blame] | 439 | ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) |
| 440 | << "Child was not stopped with a syscall stop: " << status; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 441 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 442 | struct regs regs; |
| 443 | ASSERT_TRUE(regs_read(®s, child_pid)); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 444 | |
| 445 | bool is_opening_urandom = false; |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 446 | 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 450 | // normally. Otherwise it's, e.g. -EINVAL, to indicate that the system call |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 451 | // should not run and that the given value should be injected on return. |
David Benjamin | 9ff8491 | 2024-12-03 17:00:34 -0500 | [diff] [blame] | 452 | std::optional<int> force_result; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 453 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 454 | switch (regs.syscall) { |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 455 | case __NR_getrandom: |
| 456 | if (flags & NO_GETRANDOM) { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 457 | force_result = -ENOSYS; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 458 | } else if (flags & GETRANDOM_ERROR) { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 459 | force_result = -EINVAL; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 460 | } else if (flags & GETRANDOM_NOT_READY) { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 461 | if (regs.args[2] & GRND_NONBLOCK) { |
| 462 | force_result = -EAGAIN; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 463 | } |
| 464 | } |
| 465 | out_trace->push_back( |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 466 | Event::GetRandom(/*length=*/regs.args[1], /*flags=*/regs.args[2])); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 467 | break; |
| 468 | |
| 469 | case __NR_openat: |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 470 | #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 Langley | 59fc518 | 2024-12-16 11:02:22 -0800 | [diff] [blame] | 476 | const std::string filename = |
| 477 | get_string_from_remote(child_pid, filename_ptr); |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 478 | 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 485 | if (is_opening_urandom && (flags & NO_URANDOM)) { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 486 | force_result = -ENOENT; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 487 | } |
| 488 | break; |
| 489 | } |
| 490 | |
| 491 | case __NR_read: { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 492 | const int read_fd = regs.args[0]; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 493 | if (urandom_fd >= 0 && urandom_fd == read_fd) { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 494 | out_trace->push_back(Event::UrandomRead(/*length=*/regs.args[2])); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 495 | if (flags & URANDOM_ERROR) { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 496 | 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 Langley | 4259ae8 | 2022-05-10 11:44:16 -0700 | [diff] [blame] | 512 | } |
Adam Langley | 4259ae8 | 2022-05-10 11:44:16 -0700 | [diff] [blame] | 513 | } |
| 514 | break; |
| 515 | } |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 516 | |
| 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 553 | } |
| 554 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 555 | if (force_result.has_value()) { |
| 556 | ASSERT_TRUE(regs_break_syscall(child_pid, ®s)); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 557 | } |
| 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 573 | if (force_result.has_value()) { |
| 574 | ASSERT_TRUE(regs_set_ret(child_pid, force_result.value())); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 575 | } else if (is_opening_urandom) { |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 576 | ASSERT_TRUE(regs_read(®s, child_pid)); |
| 577 | urandom_fd = regs.ret; |
| 578 | } else if (is_socket_call) { |
| 579 | ASSERT_TRUE(regs_read(®s, 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 593 | } |
| 594 | } |
| 595 | } |
| 596 | |
| 597 | // TestFunction is the function that |GetTrace| is asked to trace. |
| 598 | static void TestFunction() { |
| 599 | uint8_t byte; |
| 600 | RAND_bytes(&byte, sizeof(byte)); |
| 601 | RAND_bytes(&byte, sizeof(byte)); |
| 602 | } |
| 603 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 604 | static bool have_fork_detection() { return CRYPTO_get_fork_generation() != 0; } |
| 605 | |
| 606 | static 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 | |
| 629 | out: |
| 630 | events->push_back(Event::SocketClose()); |
| 631 | return ret; |
David Benjamin | b1086cd | 2019-02-01 00:45:23 +0000 | [diff] [blame] | 632 | } |
| 633 | |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 634 | // 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. |
| 637 | static std::vector<Event> TestFunctionPRNGModel(unsigned flags) { |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 638 | std::vector<Event> ret; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 639 | bool getrandom_ready = false; |
Adam Langley | 76cb7c5 | 2023-03-01 00:09:55 +0000 | [diff] [blame] | 640 | bool used_daemon = false; |
| 641 | |
| 642 | if (have_fork_detection()) { |
| 643 | used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags); |
| 644 | } |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 645 | |
| 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 652 | if (kIsFIPS) { |
David Benjamin | 19009c5 | 2022-08-29 13:05:06 -0400 | [diff] [blame] | 653 | // FIPS builds require getrandom. |
| 654 | ret.push_back(Event::Abort()); |
| 655 | return ret; |
| 656 | } |
| 657 | |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 658 | ret.push_back(Event::Open("/dev/urandom")); |
| 659 | if (flags & NO_URANDOM) { |
| 660 | ret.push_back(Event::Abort()); |
| 661 | return ret; |
| 662 | } |
| 663 | |
David Benjamin | 19009c5 | 2022-08-29 13:05:06 -0400 | [diff] [blame] | 664 | sysrand = [&ret, flags](bool block, size_t len) { |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 665 | 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 697 | const size_t kSeedLength = CTR_DRBG_ENTROPY_LEN * (kIsFIPS ? 10 : 1); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 698 | const size_t kAdditionalDataLength = 32; |
| 699 | |
| 700 | if (!have_rdrand()) { |
Adam Langley | 76cb7c5 | 2023-03-01 00:09:55 +0000 | [diff] [blame] | 701 | if (!have_fork_detection()) { |
| 702 | if (!sysrand(true, kAdditionalDataLength)) { |
| 703 | return ret; |
| 704 | } |
| 705 | used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags); |
| 706 | } |
Adam Langley | 59fc518 | 2024-12-16 11:02:22 -0800 | [diff] [blame] | 707 | if ( // Initialise CRNGT. |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 708 | (!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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 711 | // Second entropy draw. |
David Benjamin | b1086cd | 2019-02-01 00:45:23 +0000 | [diff] [blame] | 712 | (!have_fork_detection() && !sysrand(true, kAdditionalDataLength))) { |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 713 | return ret; |
| 714 | } |
Adam Langley | 7a22a65 | 2020-04-07 10:53:24 -0700 | [diff] [blame] | 715 | } else if ( |
| 716 | // First additional data. If fast RDRAND isn't available then a |
| 717 | // non-blocking OS entropy draw will be tried. |
David Benjamin | b1086cd | 2019-02-01 00:45:23 +0000 | [diff] [blame] | 718 | (!have_fast_rdrand() && !have_fork_detection() && |
| 719 | !sysrand(false, kAdditionalDataLength)) || |
Adam Langley | 7a22a65 | 2020-04-07 10:53:24 -0700 | [diff] [blame] | 720 | // 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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 723 | !sysrand(!kIsFIPS, CTR_DRBG_ENTROPY_LEN) || |
Adam Langley | 7a22a65 | 2020-04-07 10:53:24 -0700 | [diff] [blame] | 724 | // Second entropy draw's additional data. |
David Benjamin | b1086cd | 2019-02-01 00:45:23 +0000 | [diff] [blame] | 725 | (!have_fast_rdrand() && !have_fork_detection() && |
| 726 | !sysrand(false, kAdditionalDataLength))) { |
Adam Langley | 7a22a65 | 2020-04-07 10:53:24 -0700 | [diff] [blame] | 727 | return ret; |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 728 | } |
| 729 | |
| 730 | return ret; |
| 731 | } |
| 732 | |
Adam Langley | 7a22a65 | 2020-04-07 10:53:24 -0700 | [diff] [blame] | 733 | static 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 Langley | 7a22a65 | 2020-04-07 10:53:24 -0700 | [diff] [blame] | 747 | default: |
| 748 | break; |
| 749 | } |
| 750 | } |
| 751 | } |
| 752 | #endif |
| 753 | } |
| 754 | |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 755 | // Tests that |TestFunctionPRNGModel| is a correct model for the code in |
| 756 | // urandom.c, at least to the limits of the the |Event| type. |
| 757 | TEST(URandomTest, Test) { |
| 758 | char buf[256]; |
| 759 | |
Adam Langley | 3cd7faa | 2023-02-23 00:23:27 +0000 | [diff] [blame] | 760 | // 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 Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 766 | #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 Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 771 | 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 Langley | 3cd7faa | 2023-02-23 00:23:27 +0000 | [diff] [blame] | 778 | if (!has_getrandom && !(flags & NO_GETRANDOM)) { |
Adam Langley | 59fc518 | 2024-12-16 11:02:22 -0800 | [diff] [blame] | 779 | continue; |
Adam Langley | 3cd7faa | 2023-02-23 00:23:27 +0000 | [diff] [blame] | 780 | } |
| 781 | |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 782 | TRACE_FLAG(NO_GETRANDOM); |
| 783 | TRACE_FLAG(NO_URANDOM); |
| 784 | TRACE_FLAG(GETRANDOM_NOT_READY); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 785 | TRACE_FLAG(GETRANDOM_ERROR); |
| 786 | TRACE_FLAG(URANDOM_ERROR); |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 787 | TRACE_FLAG(SOCKET_ERROR); |
| 788 | TRACE_FLAG(CONNECT_ERROR); |
| 789 | TRACE_FLAG(SOCKET_READ_ERROR); |
| 790 | TRACE_FLAG(SOCKET_READ_SHORT); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 791 | |
| 792 | const std::vector<Event> expected_trace = TestFunctionPRNGModel(flags); |
Adam Langley | 7a22a65 | 2020-04-07 10:53:24 -0700 | [diff] [blame] | 793 | CheckInvariants(expected_trace); |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 794 | 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 Langley | 59fc518 | 2024-12-16 11:02:22 -0800 | [diff] [blame] | 804 | } // namespace |
| 805 | |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 806 | int main(int argc, char **argv) { |
| 807 | ::testing::InitGoogleTest(&argc, argv); |
David Benjamin | b1086cd | 2019-02-01 00:45:23 +0000 | [diff] [blame] | 808 | |
| 809 | if (getenv("BORINGSSL_IGNORE_MADV_WIPEONFORK")) { |
Adam Langley | 76cb7c5 | 2023-03-01 00:09:55 +0000 | [diff] [blame] | 810 | CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(0); |
| 811 | } else { |
| 812 | CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(1); |
David Benjamin | b1086cd | 2019-02-01 00:45:23 +0000 | [diff] [blame] | 813 | } |
| 814 | |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 815 | return RUN_ALL_TESTS(); |
| 816 | } |
| 817 | |
| 818 | #else |
| 819 | |
Adam Langley | 7de9498 | 2019-10-17 09:15:37 -0700 | [diff] [blame] | 820 | int main(int argc, char **argv) { |
| 821 | printf("PASS\n"); |
| 822 | return 0; |
| 823 | } |
Adam Langley | 3e502c8 | 2019-10-16 09:56:38 -0700 | [diff] [blame] | 824 | |
Adam Langley | 85a1e2e | 2022-07-29 18:12:37 +0000 | [diff] [blame] | 825 | #endif // (X86_64 || AARCH64) && !SHARED_LIBRARY && |
| 826 | // !UNSAFE_DETERMINISTIC_MODE && USE_NR_getrandom |