| /* Copyright (c) 2019, Google Inc. | 
 |  * | 
 |  * Permission to use, copy, modify, and/or distribute this software for any | 
 |  * purpose with or without fee is hereby granted, provided that the above | 
 |  * copyright notice and this permission notice appear in all copies. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
 |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
 |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | 
 |  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
 |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | 
 |  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | 
 |  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ | 
 |  | 
 | #include <gtest/gtest.h> | 
 | #include <stdlib.h> | 
 |  | 
 | #include <openssl/bytestring.h> | 
 | #include <openssl/ctrdrbg.h> | 
 | #include <openssl/rand.h> | 
 |  | 
 | #include "getrandom_fillin.h" | 
 | #include "internal.h" | 
 |  | 
 | #if (defined(OPENSSL_X86_64) || defined(OPENSSL_AARCH64)) &&               \ | 
 |     !defined(BORINGSSL_SHARED_LIBRARY) && defined(OPENSSL_RAND_URANDOM) && \ | 
 |     defined(USE_NR_getrandom) | 
 |  | 
 | #include <elf.h> | 
 | #include <linux/random.h> | 
 | #include <sys/ptrace.h> | 
 | #include <sys/socket.h> | 
 | #include <sys/syscall.h> | 
 | #include <sys/uio.h> | 
 | #include <sys/un.h> | 
 | #include <sys/user.h> | 
 |  | 
 | #include "fork_detect.h" | 
 | #include "getrandom_fillin.h" | 
 |  | 
 | #if !defined(PTRACE_O_EXITKILL) | 
 | #define PTRACE_O_EXITKILL (1 << 20) | 
 | #endif | 
 |  | 
 | #if defined(OPENSSL_ANDROID) | 
 | static const bool kIsAndroid = true; | 
 | #else | 
 | static const bool kIsAndroid = false; | 
 | #endif | 
 |  | 
 | #if defined(BORINGSSL_FIPS) | 
 | static const bool kIsFIPS = true; | 
 | #else | 
 | static const bool kIsFIPS = false; | 
 | #endif | 
 |  | 
 | static const bool kUsesDaemon = kIsFIPS && kIsAndroid; | 
 |  | 
 | // kDaemonWriteLength is the number of bytes that the entropy daemon writes. | 
 | static const size_t kDaemonWriteLength = 496; | 
 |  | 
 | // This test can be run with $OPENSSL_ia32cap=~0x4000000000000000 in order to | 
 | // simulate the absence of RDRAND of machines that have it. | 
 |  | 
 | // Event represents a system call from urandom.c that is observed by the ptrace | 
 | // code in |GetTrace|. | 
 | struct Event { | 
 |   enum class Syscall { | 
 |     kGetRandom, | 
 |     kOpen, | 
 |     kUrandomRead, | 
 |     kSocket, | 
 |     kConnect, | 
 |     kSocketRead, | 
 |     kSocketClose, | 
 |     kAbort, | 
 |   }; | 
 |  | 
 |   explicit Event(Syscall syscall) : type(syscall) {} | 
 |  | 
 |   bool operator==(const Event &other) const { | 
 |     return type == other.type && length == other.length && | 
 |            flags == other.flags && | 
 |            filename == other.filename; | 
 |   } | 
 |  | 
 |   static Event GetRandom(size_t length, unsigned flags) { | 
 |     Event e(Syscall::kGetRandom); | 
 |     e.length = length; | 
 |     e.flags = flags; | 
 |     return e; | 
 |   } | 
 |  | 
 |   static Event Open(const std::string &filename) { | 
 |     Event e(Syscall::kOpen); | 
 |     e.filename = filename; | 
 |     return e; | 
 |   } | 
 |  | 
 |   static Event UrandomRead(size_t length) { | 
 |     Event e(Syscall::kUrandomRead); | 
 |     e.length = length; | 
 |     return e; | 
 |   } | 
 |  | 
 |   static Event Socket() { | 
 |     Event e(Syscall::kSocket); | 
 |     return e; | 
 |   } | 
 |  | 
 |   static Event Connect() { | 
 |     Event e(Syscall::kConnect); | 
 |     return e; | 
 |   } | 
 |  | 
 |   static Event SocketRead(size_t length) { | 
 |     Event e(Syscall::kSocketRead); | 
 |     e.length = length; | 
 |     return e; | 
 |   } | 
 |  | 
 |   static Event SocketClose() { | 
 |     Event e(Syscall::kSocketClose); | 
 |     return e; | 
 |   } | 
 |  | 
 |   static Event Abort() { | 
 |     Event e(Syscall::kAbort); | 
 |     return e; | 
 |   } | 
 |  | 
 |   std::string String() const { | 
 |     char buf[256]; | 
 |  | 
 |     switch (type) { | 
 |       case Syscall::kGetRandom: | 
 |         snprintf(buf, sizeof(buf), "getrandom(_, %zu, %u)", length, flags); | 
 |         break; | 
 |  | 
 |       case Syscall::kOpen: | 
 |         snprintf(buf, sizeof(buf), "open(%s, _)", filename.c_str()); | 
 |         break; | 
 |  | 
 |       case Syscall::kUrandomRead: | 
 |         snprintf(buf, sizeof(buf), "read(urandom_fd, _, %zu)", length); | 
 |         break; | 
 |  | 
 |       case Syscall::kSocket: | 
 |         return "socket(UNIX, STREAM, _)"; | 
 |  | 
 |       case Syscall::kConnect: | 
 |         return "connect(sock, _, _)"; | 
 |  | 
 |       case Syscall::kSocketRead: | 
 |         snprintf(buf, sizeof(buf), "read(sock_fd, _, %zu)", length); | 
 |         break; | 
 |  | 
 |       case Syscall::kSocketClose: | 
 |         return "close(sock)"; | 
 |  | 
 |       case Syscall::kAbort: | 
 |         return "abort()"; | 
 |     } | 
 |  | 
 |     return std::string(buf); | 
 |   } | 
 |  | 
 |   const Syscall type; | 
 |   size_t length = 0; | 
 |   unsigned flags = 0; | 
 |   std::string filename; | 
 | }; | 
 |  | 
 | static std::string ToString(const std::vector<Event> &trace) { | 
 |   std::string ret; | 
 |   for (const auto &event : trace) { | 
 |     if (!ret.empty()) { | 
 |       ret += ", "; | 
 |     } | 
 |     ret += event.String(); | 
 |   } | 
 |   return ret; | 
 | } | 
 |  | 
 | // The following are flags to tell |GetTrace| to inject faults, using ptrace, | 
 | // into the entropy-related system calls. | 
 |  | 
 | // getrandom gives |ENOSYS|. | 
 | static const unsigned NO_GETRANDOM = 1; | 
 | // opening /dev/urandom fails. | 
 | static const unsigned NO_URANDOM = 2; | 
 | // getrandom always returns |EAGAIN| if given |GRNG_NONBLOCK|. | 
 | static const unsigned GETRANDOM_NOT_READY = 4; | 
 | // getrandom gives |EINVAL| unless |NO_GETRANDOM| is set. | 
 | static const unsigned GETRANDOM_ERROR = 8; | 
 | // Reading from /dev/urandom gives |EINVAL|. | 
 | static const unsigned URANDOM_ERROR = 16; | 
 | static const unsigned SOCKET_ERROR = 32; | 
 | static const unsigned CONNECT_ERROR = 64; | 
 | static const unsigned SOCKET_READ_ERROR = 128; | 
 | static const unsigned SOCKET_READ_SHORT = 256; | 
 | static const unsigned NEXT_FLAG = 512; | 
 |  | 
 | // regs_read fetches the registers of |child_pid| and writes them to |out_regs|. | 
 | // That structure will contain at least the following members: | 
 | //   syscall: the syscall number, if registers were read just before entering | 
 | //       one. | 
 | //   args[0..2]: syscall arguments, if registers were read just before | 
 | //       entering one. | 
 | //   ret: the syscall return value, if registers were read just after finishing | 
 | //       one. | 
 | // | 
 | // This call returns true on success and false otherwise. | 
 | static bool regs_read(struct regs *out_regs, int child_pid); | 
 |  | 
 | // regs_set_ret sets the return value of the system call that |child_pid| has | 
 | // just finished, to |ret|. It returns true on success and false otherwise. | 
 | static bool regs_set_ret(int child_pid, int ret); | 
 |  | 
 | // regs_break_syscall causes the system call that |child_pid| is about to enter | 
 | // to fail to run. | 
 | static bool regs_break_syscall(int child_pid, const struct regs *orig_regs); | 
 |  | 
 | struct regs { | 
 |   uintptr_t syscall; | 
 |   uintptr_t args[3]; | 
 |   uintptr_t ret; | 
 |   struct user_regs_struct regs; | 
 | }; | 
 |  | 
 | #if defined(OPENSSL_X86_64) | 
 |  | 
 | static bool regs_read(struct regs *out_regs, int child_pid) { | 
 |   if (ptrace(PTRACE_GETREGS, child_pid, nullptr, &out_regs->regs) != 0) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   out_regs->syscall = out_regs->regs.orig_rax; | 
 |   out_regs->ret = out_regs->regs.rax; | 
 |   out_regs->args[0] = out_regs->regs.rdi; | 
 |   out_regs->args[1] = out_regs->regs.rsi; | 
 |   out_regs->args[2] = out_regs->regs.rdx; | 
 |   return true; | 
 | } | 
 |  | 
 | static bool regs_set_ret(int child_pid, int ret) { | 
 |   struct regs regs; | 
 |   if (!regs_read(®s, child_pid)) { | 
 |     return false; | 
 |   } | 
 |   regs.regs.rax = ret; | 
 |   return ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s.regs) == 0; | 
 | } | 
 |  | 
 | static bool regs_break_syscall(int child_pid, const struct regs *orig_regs) { | 
 |   // Replace the syscall number with -1 to cause the kernel to fail the call. | 
 |   struct user_regs_struct regs = orig_regs->regs; | 
 |   regs.orig_rax = -1; | 
 |   return ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s) == 0; | 
 | } | 
 |  | 
 | #elif defined(OPENSSL_AARCH64) | 
 |  | 
 | static bool regs_read(struct regs *out_regs, int child_pid) { | 
 |   struct iovec io; | 
 |   io.iov_base = &out_regs->regs; | 
 |   io.iov_len = sizeof(out_regs->regs); | 
 |   if (ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &io) != 0) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   out_regs->syscall = out_regs->regs.regs[8]; | 
 |   out_regs->ret = out_regs->regs.regs[0]; | 
 |   out_regs->args[0] = out_regs->regs.regs[0]; | 
 |   out_regs->args[1] = out_regs->regs.regs[1]; | 
 |   out_regs->args[2] = out_regs->regs.regs[2]; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | static bool set_regset(int child_pid, int regset, const void *data, | 
 |                        size_t len) { | 
 |   struct iovec io; | 
 |   io.iov_base = const_cast<void *>(data); | 
 |   io.iov_len = len; | 
 |   return ptrace(PTRACE_SETREGSET, child_pid, reinterpret_cast<void *>(regset), | 
 |                 &io) == 0; | 
 | } | 
 |  | 
 | static bool regs_set_ret(int child_pid, int ret) { | 
 |   struct regs regs; | 
 |   if (!regs_read(®s, child_pid)) { | 
 |     return false; | 
 |   } | 
 |   regs.regs.regs[0] = ret; | 
 |   return set_regset(child_pid, NT_PRSTATUS, ®s.regs, sizeof(regs.regs)); | 
 | } | 
 |  | 
 | static bool regs_break_syscall(int child_pid, const struct regs *orig_regs) { | 
 |   // Replace the syscall number with -1 to cause the kernel to fail the call. | 
 |   int syscall = -1; | 
 |   return set_regset(child_pid, NT_ARM_SYSTEM_CALL, &syscall, sizeof(syscall)); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | // SyscallResult is like std::optional<int>. | 
 | // TODO: use std::optional when we can use C++17. | 
 | class SyscallResult { | 
 |  public: | 
 |   SyscallResult &operator=(int value) { | 
 |     has_value_ = true; | 
 |     value_ = value; | 
 |     return *this; | 
 |   } | 
 |  | 
 |   int value() const { | 
 |     if (!has_value_) { | 
 |       abort(); | 
 |     } | 
 |     return value_; | 
 |   } | 
 |  | 
 |   bool has_value() const { return has_value_; } | 
 |  | 
 |  private: | 
 |   bool has_value_ = false; | 
 |   int value_ = 0; | 
 | }; | 
 |  | 
 | // memcpy_to_remote copies |n| bytes from |in_src| in the local address space, | 
 | // to |dest| in the address space of |child_pid|. | 
 | static void memcpy_to_remote(int child_pid, uint64_t dest, const void *in_src, | 
 |                              size_t n) { | 
 |   const uint8_t *src = reinterpret_cast<const uint8_t *>(in_src); | 
 |  | 
 |   // ptrace always works with ill-defined "words", which appear to be 64-bit | 
 |   // on 64-bit systems. | 
 | #if !defined(OPENSSL_64_BIT) | 
 | #error "This code probably doesn't work" | 
 | #endif | 
 |  | 
 |   while (n) { | 
 |     const uintptr_t aligned_addr = dest & ~7; | 
 |     const uintptr_t offset = dest - aligned_addr; | 
 |     const size_t space = 8 - offset; | 
 |     size_t todo = n; | 
 |     if (todo > space) { | 
 |       todo = space; | 
 |     } | 
 |  | 
 |     uint64_t word; | 
 |     if (offset == 0 && todo == 8) { | 
 |       word = CRYPTO_load_u64_le(src); | 
 |     } else { | 
 |       uint8_t bytes[8]; | 
 |       CRYPTO_store_u64_le( | 
 |           bytes, ptrace(PTRACE_PEEKDATA, child_pid, | 
 |                         reinterpret_cast<void *>(aligned_addr), nullptr)); | 
 |       memcpy(&bytes[offset], src, todo); | 
 |       word = CRYPTO_load_u64_le(bytes); | 
 |     } | 
 |  | 
 |     ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, child_pid, | 
 |                         reinterpret_cast<void *>(aligned_addr), | 
 |                         reinterpret_cast<void *>(word))); | 
 |  | 
 |     src += todo; | 
 |     n -= todo; | 
 |     dest += todo; | 
 |   } | 
 | } | 
 |  | 
 | static uint8_t get_byte_from_remote(int child_pid, uint64_t ptr) { | 
 |   // ptrace always works with ill-defined "words", which appear to be 64-bit | 
 |   // on 64-bit systems. | 
 | #if !defined(OPENSSL_64_BIT) | 
 | #error "This code probably doesn't work" | 
 | #endif | 
 |  | 
 |   const uintptr_t aligned_addr = ptr & ~7; | 
 |   const uintptr_t offset = ptr - aligned_addr; | 
 |  | 
 |   uint64_t word = ptrace(PTRACE_PEEKDATA, child_pid, | 
 |                          reinterpret_cast<void *>(aligned_addr), 0); | 
 |   uint8_t bytes[8]; | 
 |   CRYPTO_store_u64_le(bytes, word); | 
 |   return bytes[offset]; | 
 | } | 
 |  | 
 | static std::string get_string_from_remote(int child_pid, uint64_t ptr) { | 
 |   std::string ret; | 
 |  | 
 |   for (;;) { | 
 |     const uint8_t byte = get_byte_from_remote(child_pid, ptr); | 
 |     if (byte == 0) { | 
 |       break; | 
 |     } | 
 |     ret.push_back((char)byte); | 
 |     ptr++; | 
 |   } | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | // GetTrace runs |thunk| in a forked process and observes the resulting system | 
 | // calls using ptrace. It simulates a variety of failures based on the contents | 
 | // of |flags| and records the observed events by appending to |out_trace|. | 
 | static void GetTrace(std::vector<Event> *out_trace, unsigned flags, | 
 |                      std::function<void()> thunk) { | 
 |   const int child_pid = fork(); | 
 |   ASSERT_NE(-1, child_pid); | 
 |  | 
 |   if (child_pid == 0) { | 
 |     // Child process | 
 |     if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) { | 
 |       perror("PTRACE_TRACEME"); | 
 |       _exit(1); | 
 |     } | 
 |     raise(SIGSTOP); | 
 |     thunk(); | 
 |     _exit(0); | 
 |   } | 
 |  | 
 |   // Parent process | 
 |   int status; | 
 |   ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); | 
 |   ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) | 
 |       << "Child was not stopped with SIGSTOP: " << status; | 
 |  | 
 |   // Set options so that: | 
 |   //   a) the child process is killed once this process dies. | 
 |   //   b) System calls result in a WSTOPSIG value of (SIGTRAP | 0x80) rather | 
 |   //      than just SIGTRAP. (This doesn't matter here, but it's recommended | 
 |   //      practice so that it's distinct from the signal itself.) | 
 |   ASSERT_EQ(0, ptrace(PTRACE_SETOPTIONS, child_pid, nullptr, | 
 |                       PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD)) | 
 |       << strerror(errno); | 
 |  | 
 |   // urandom_fd tracks the file descriptor number for /dev/urandom in the child | 
 |   // process, if it opens it. | 
 |   int urandom_fd = -1; | 
 |  | 
 |   // sock_fd tracks the file descriptor number for the socket to the entropy | 
 |   // daemon, if one is opened. | 
 |   int sock_fd = -1; | 
 |  | 
 |   for (;;) { | 
 |     // Advance the child to the next system call. | 
 |     ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0)); | 
 |     ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); | 
 |  | 
 |     // The child may have aborted rather than made a system call. | 
 |     if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGABRT) { | 
 |       out_trace->push_back(Event::Abort()); | 
 |       break; | 
 |     } | 
 |  | 
 |     // Otherwise the only valid ptrace event is a system call stop. | 
 |     ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) | 
 |         << "Child was not stopped with a syscall stop: " << status; | 
 |  | 
 |     struct regs regs; | 
 |     ASSERT_TRUE(regs_read(®s, child_pid)); | 
 |  | 
 |     bool is_opening_urandom = false; | 
 |     bool is_socket_call = false; | 
 |     bool is_socket_read = false; | 
 |     uint64_t socket_read_bytes = 0; | 
 |     // force_result is unset to indicate that the system call should run | 
 |     // normally. Otherwise it's, e.g. -EINVAL, to indicate that the system call | 
 |     // should not run and that the given value should be injected on return. | 
 |     SyscallResult force_result; | 
 |  | 
 |     switch (regs.syscall) { | 
 |       case __NR_getrandom: | 
 |         if (flags & NO_GETRANDOM) { | 
 |           force_result = -ENOSYS; | 
 |         } else if (flags & GETRANDOM_ERROR) { | 
 |           force_result = -EINVAL; | 
 |         } else if (flags & GETRANDOM_NOT_READY) { | 
 |           if (regs.args[2] & GRND_NONBLOCK) { | 
 |             force_result = -EAGAIN; | 
 |           } | 
 |         } | 
 |         out_trace->push_back( | 
 |             Event::GetRandom(/*length=*/regs.args[1], /*flags=*/regs.args[2])); | 
 |         break; | 
 |  | 
 |       case __NR_openat: | 
 | #if defined(OPENSSL_X86_64) | 
 |       case __NR_open: | 
 | #endif | 
 |       { | 
 |         uintptr_t filename_ptr = | 
 |             (regs.syscall == __NR_openat) ? regs.args[1] : regs.args[0]; | 
 |         const std::string filename = get_string_from_remote(child_pid, filename_ptr); | 
 |         if (filename.find("/dev/__properties__/") == 0) { | 
 |           // Android may try opening these files as part of SELinux support. | 
 |           // They are ignored here. | 
 |         } else { | 
 |           out_trace->push_back(Event::Open(filename)); | 
 |         } | 
 |         is_opening_urandom = (filename == "/dev/urandom"); | 
 |         if (is_opening_urandom && (flags & NO_URANDOM)) { | 
 |           force_result = -ENOENT; | 
 |         } | 
 |         break; | 
 |       } | 
 |  | 
 |       case __NR_read: { | 
 |         const int read_fd = regs.args[0]; | 
 |         if (urandom_fd >= 0 && urandom_fd == read_fd) { | 
 |           out_trace->push_back(Event::UrandomRead(/*length=*/regs.args[2])); | 
 |           if (flags & URANDOM_ERROR) { | 
 |             force_result = -EINVAL; | 
 |           } | 
 |         } else if (sock_fd >= 0 && sock_fd == read_fd) { | 
 |           uint64_t length = regs.args[2]; | 
 |           out_trace->push_back(Event::SocketRead(length)); | 
 |           if (flags & SOCKET_READ_ERROR) { | 
 |             force_result = -EINVAL; | 
 |           } else { | 
 |             is_socket_read = true; | 
 |             socket_read_bytes = length; | 
 |  | 
 |             if (flags & SOCKET_READ_SHORT) { | 
 |               ASSERT_GT(socket_read_bytes, 0u); | 
 |               socket_read_bytes--; | 
 |               flags &= ~SOCKET_READ_SHORT; | 
 |             } | 
 |           } | 
 |         } | 
 |         break; | 
 |       } | 
 |  | 
 |       case __NR_close: { | 
 |         if (sock_fd >= 0 && static_cast<int>(regs.args[0]) == sock_fd) { | 
 |           out_trace->push_back(Event::SocketClose()); | 
 |           sock_fd = -1; | 
 |         } | 
 |         break; | 
 |       } | 
 |  | 
 |       case __NR_socket: { | 
 |         const int family = regs.args[0]; | 
 |         const int type = regs.args[1]; | 
 |         if (family == AF_UNIX && type == SOCK_STREAM) { | 
 |           out_trace->push_back(Event::Socket()); | 
 |           is_socket_call = true; | 
 |           if (flags & SOCKET_ERROR) { | 
 |             force_result = -EINVAL; | 
 |           } | 
 |         } | 
 |         break; | 
 |       } | 
 |  | 
 |       case __NR_connect: { | 
 |         const int connect_fd = regs.args[0]; | 
 |         if (sock_fd >= 0 && connect_fd == sock_fd) { | 
 |           out_trace->push_back(Event::Connect()); | 
 |           if (flags & CONNECT_ERROR) { | 
 |             force_result = -EINVAL; | 
 |           } else { | 
 |             // The test system might not have an entropy daemon running so | 
 |             // inject a success result. | 
 |             force_result = 0; | 
 |           } | 
 |         } | 
 |  | 
 |         break; | 
 |       } | 
 |     } | 
 |  | 
 |     if (force_result.has_value()) { | 
 |       ASSERT_TRUE(regs_break_syscall(child_pid, ®s)); | 
 |     } | 
 |  | 
 |     ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0)); | 
 |     ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); | 
 |     // If the system call was exit/exit_group, the process may be terminated | 
 |     // rather than have exited the system call. | 
 |     if (WIFEXITED(status)) { | 
 |       ASSERT_EQ(0, WEXITSTATUS(status)); | 
 |       return; | 
 |     } | 
 |  | 
 |     // Otherwise the next state must be a system call exit stop. This is | 
 |     // indistinguishable from a system call entry, we just have to keep track | 
 |     // and know that these events happen in pairs. | 
 |     ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)); | 
 |  | 
 |     if (force_result.has_value()) { | 
 |       ASSERT_TRUE(regs_set_ret(child_pid, force_result.value())); | 
 |     } else if (is_opening_urandom) { | 
 |       ASSERT_TRUE(regs_read(®s, child_pid)); | 
 |       urandom_fd = regs.ret; | 
 |     } else if (is_socket_call) { | 
 |       ASSERT_TRUE(regs_read(®s, child_pid)); | 
 |       sock_fd = regs.ret; | 
 |     } else if (is_socket_read) { | 
 |       // Simulate a response from the entropy daemon since it might not be | 
 |       // running on the current system. | 
 |       uint8_t entropy[kDaemonWriteLength]; | 
 |       ASSERT_LE(socket_read_bytes, sizeof(entropy)); | 
 |  | 
 |       for (size_t i = 0; i < sizeof(entropy); i++) { | 
 |         entropy[i] = i & 0xff; | 
 |       } | 
 |       memcpy_to_remote(child_pid, regs.args[1], entropy, socket_read_bytes); | 
 |  | 
 |       ASSERT_TRUE(regs_set_ret(child_pid, socket_read_bytes)); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | // TestFunction is the function that |GetTrace| is asked to trace. | 
 | static void TestFunction() { | 
 |   uint8_t byte; | 
 |   RAND_bytes(&byte, sizeof(byte)); | 
 |   RAND_bytes(&byte, sizeof(byte)); | 
 | } | 
 |  | 
 | static bool have_fork_detection() { return CRYPTO_get_fork_generation() != 0; } | 
 |  | 
 | static bool AppendDaemonEvents(std::vector<Event> *events, unsigned flags) { | 
 |   events->push_back(Event::Socket()); | 
 |   if (flags & SOCKET_ERROR) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool ret = false; | 
 |   events->push_back(Event::Connect()); | 
 |   if (flags & CONNECT_ERROR) { | 
 |     goto out; | 
 |   } | 
 |  | 
 |   events->push_back(Event::SocketRead(kDaemonWriteLength)); | 
 |   if (flags & SOCKET_READ_ERROR) { | 
 |     goto out; | 
 |   } | 
 |  | 
 |   if (flags & SOCKET_READ_SHORT) { | 
 |     events->push_back(Event::SocketRead(1)); | 
 |   } | 
 |  | 
 |   ret = true; | 
 |  | 
 | out: | 
 |   events->push_back(Event::SocketClose()); | 
 |   return ret; | 
 | } | 
 |  | 
 | // TestFunctionPRNGModel is a model of how the urandom.c code will behave when | 
 | // |TestFunction| is run. It should return the same trace of events that | 
 | // |GetTrace| will observe the real code making. | 
 | static std::vector<Event> TestFunctionPRNGModel(unsigned flags) { | 
 |   std::vector<Event> ret; | 
 |   bool getrandom_ready = false; | 
 |   bool used_daemon = false; | 
 |  | 
 |   if (have_fork_detection()) { | 
 |     used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags); | 
 |   } | 
 |  | 
 |   // Probe for getrandom support | 
 |   ret.push_back(Event::GetRandom(1, GRND_NONBLOCK)); | 
 |   std::function<void()> wait_for_entropy; | 
 |   std::function<bool(bool, size_t)> sysrand; | 
 |  | 
 |   if (flags & NO_GETRANDOM) { | 
 |     if (kIsFIPS) { | 
 |       // FIPS builds require getrandom. | 
 |       ret.push_back(Event::Abort()); | 
 |       return ret; | 
 |     } | 
 |  | 
 |     ret.push_back(Event::Open("/dev/urandom")); | 
 |     if (flags & NO_URANDOM) { | 
 |       ret.push_back(Event::Abort()); | 
 |       return ret; | 
 |     } | 
 |  | 
 |     sysrand = [&ret, flags](bool block, size_t len) { | 
 |       ret.push_back(Event::UrandomRead(len)); | 
 |       if (flags & URANDOM_ERROR) { | 
 |         ret.push_back(Event::Abort()); | 
 |         return false; | 
 |       } | 
 |       return true; | 
 |     }; | 
 |   } else { | 
 |     if (flags & GETRANDOM_ERROR) { | 
 |       ret.push_back(Event::Abort()); | 
 |       return ret; | 
 |     } | 
 |  | 
 |     getrandom_ready = (flags & GETRANDOM_NOT_READY) == 0; | 
 |     wait_for_entropy = [&ret, &getrandom_ready] { | 
 |       if (getrandom_ready) { | 
 |         return; | 
 |       } | 
 |  | 
 |       ret.push_back(Event::GetRandom(1, GRND_NONBLOCK)); | 
 |       ret.push_back(Event::GetRandom(1, 0)); | 
 |       getrandom_ready = true; | 
 |     }; | 
 |     sysrand = [&ret, &wait_for_entropy](bool block, size_t len) { | 
 |       if (block) { | 
 |         wait_for_entropy(); | 
 |       } | 
 |       ret.push_back(Event::GetRandom(len, block ? 0 : GRND_NONBLOCK)); | 
 |       return true; | 
 |     }; | 
 |   } | 
 |  | 
 |   const size_t kSeedLength = CTR_DRBG_ENTROPY_LEN * (kIsFIPS ? 10 : 1); | 
 |   const size_t kAdditionalDataLength = 32; | 
 |  | 
 |   if (!have_rdrand()) { | 
 |     if (!have_fork_detection()) { | 
 |       if (!sysrand(true, kAdditionalDataLength)) { | 
 |         return ret; | 
 |       } | 
 |       used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags); | 
 |     } | 
 |     if (// Initialise CRNGT. | 
 |         (!used_daemon && !sysrand(true, kSeedLength + (kIsFIPS ? 16 : 0))) || | 
 |         // Personalisation draw if the daemon was used. | 
 |         (used_daemon && !sysrand(false, CTR_DRBG_ENTROPY_LEN)) || | 
 |         // Second entropy draw. | 
 |         (!have_fork_detection() && !sysrand(true, kAdditionalDataLength))) { | 
 |       return ret; | 
 |     } | 
 |   } else if ( | 
 |       // First additional data. If fast RDRAND isn't available then a | 
 |       // non-blocking OS entropy draw will be tried. | 
 |       (!have_fast_rdrand() && !have_fork_detection() && | 
 |        !sysrand(false, kAdditionalDataLength)) || | 
 |       // Opportuntistic entropy draw in FIPS mode because RDRAND was used. | 
 |       // In non-FIPS mode it's just drawn from |CRYPTO_sysrand| in a blocking | 
 |       // way. | 
 |       !sysrand(!kIsFIPS, CTR_DRBG_ENTROPY_LEN) || | 
 |       // Second entropy draw's additional data. | 
 |       (!have_fast_rdrand() && !have_fork_detection() && | 
 |        !sysrand(false, kAdditionalDataLength))) { | 
 |     return ret; | 
 |   } | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | static void CheckInvariants(const std::vector<Event> &events) { | 
 |   // If RDRAND is available then there should be no blocking syscalls in FIPS | 
 |   // mode. | 
 | #if defined(BORINGSSL_FIPS) | 
 |   if (have_rdrand()) { | 
 |     for (const auto &event : events) { | 
 |       switch (event.type) { | 
 |         case Event::Syscall::kGetRandom: | 
 |           if ((event.flags & GRND_NONBLOCK) == 0) { | 
 |             ADD_FAILURE() << "Blocking getrandom found with RDRAND: " | 
 |                           << ToString(events); | 
 |           } | 
 |           break; | 
 |  | 
 |         default: | 
 |           break; | 
 |       } | 
 |     } | 
 |   } | 
 | #endif | 
 | } | 
 |  | 
 | // Tests that |TestFunctionPRNGModel| is a correct model for the code in | 
 | // urandom.c, at least to the limits of the the |Event| type. | 
 | TEST(URandomTest, Test) { | 
 |   char buf[256]; | 
 |  | 
 |   // Some Android systems lack getrandom. | 
 |   uint8_t scratch[1]; | 
 |   const bool has_getrandom = | 
 |       (syscall(__NR_getrandom, scratch, sizeof(scratch), GRND_NONBLOCK) != -1 || | 
 |        errno != ENOSYS); | 
 |  | 
 | #define TRACE_FLAG(flag)                                         \ | 
 |   snprintf(buf, sizeof(buf), #flag ": %d", (flags & flag) != 0); \ | 
 |   SCOPED_TRACE(buf); | 
 |  | 
 |   for (unsigned flags = 0; flags < NEXT_FLAG; flags++) { | 
 |     if (!kUsesDaemon && (flags & (SOCKET_ERROR | CONNECT_ERROR | | 
 |                                   SOCKET_READ_ERROR | SOCKET_READ_SHORT))) { | 
 |       // These cases are meaningless unless the code will try to use the entropy | 
 |       // daemon. | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (!has_getrandom && !(flags & NO_GETRANDOM)) { | 
 |         continue; | 
 |     } | 
 |  | 
 |     TRACE_FLAG(NO_GETRANDOM); | 
 |     TRACE_FLAG(NO_URANDOM); | 
 |     TRACE_FLAG(GETRANDOM_NOT_READY); | 
 |     TRACE_FLAG(GETRANDOM_ERROR); | 
 |     TRACE_FLAG(URANDOM_ERROR); | 
 |     TRACE_FLAG(SOCKET_ERROR); | 
 |     TRACE_FLAG(CONNECT_ERROR); | 
 |     TRACE_FLAG(SOCKET_READ_ERROR); | 
 |     TRACE_FLAG(SOCKET_READ_SHORT); | 
 |  | 
 |     const std::vector<Event> expected_trace = TestFunctionPRNGModel(flags); | 
 |     CheckInvariants(expected_trace); | 
 |     std::vector<Event> actual_trace; | 
 |     GetTrace(&actual_trace, flags, TestFunction); | 
 |  | 
 |     if (expected_trace != actual_trace) { | 
 |       ADD_FAILURE() << "Expected: " << ToString(expected_trace) | 
 |                     << "\nFound:    " << ToString(actual_trace); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | int main(int argc, char **argv) { | 
 |   ::testing::InitGoogleTest(&argc, argv); | 
 |  | 
 |   if (getenv("BORINGSSL_IGNORE_MADV_WIPEONFORK")) { | 
 |     CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(0); | 
 |   } else { | 
 |     CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(1); | 
 |   } | 
 |  | 
 |   return RUN_ALL_TESTS(); | 
 | } | 
 |  | 
 | #else | 
 |  | 
 | int main(int argc, char **argv) { | 
 |   printf("PASS\n"); | 
 |   return 0; | 
 | } | 
 |  | 
 | #endif  // (X86_64 || AARCH64) && !SHARED_LIBRARY && | 
 |         // !UNSAFE_DETERMINISTIC_MODE && USE_NR_getrandom |