| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/rand_util.h" | 
 |  | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include "base/check.h" | 
 | #include "base/compiler_specific.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/no_destructor.h" | 
 | #include "base/posix/eintr_wrapper.h" | 
 | #include "build/build_config.h" | 
 |  | 
 | #if defined(OS_LINUX) || defined(OS_CHROMEOS) | 
 | #include "third_party/lss/linux_syscall_support.h" | 
 | #endif | 
 |  | 
 | #if !defined(OS_IOS) && !defined(OS_NACL) | 
 | // TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK. | 
 | // (See below.) | 
 | #include <sys/random.h> | 
 | #endif | 
 |  | 
 | namespace { | 
 |  | 
 | #if defined(OS_AIX) | 
 | // AIX has no 64-bit support for O_CLOEXEC. | 
 | static constexpr int kOpenFlags = O_RDONLY; | 
 | #else | 
 | static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC; | 
 | #endif | 
 |  | 
 | // We keep the file descriptor for /dev/urandom around so we don't need to | 
 | // reopen it (which is expensive), and since we may not even be able to reopen | 
 | // it if we are later put in a sandbox. This class wraps the file descriptor so | 
 | // we can use a static-local variable to handle opening it on the first access. | 
 | class URandomFd { | 
 |  public: | 
 |   URandomFd() : fd_(HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) { | 
 |     CHECK(fd_ >= 0) << "Cannot open /dev/urandom"; | 
 |   } | 
 |  | 
 |   ~URandomFd() { close(fd_); } | 
 |  | 
 |   int fd() const { return fd_; } | 
 |  | 
 |  private: | 
 |   const int fd_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | namespace base { | 
 |  | 
 | // NOTE: In an ideal future, all implementations of this function will just | 
 | // wrap BoringSSL's `RAND_bytes`. TODO(crbug.com/995996): Figure out the | 
 | // build/test/performance issues with dcheng's CL | 
 | // (https://chromium-review.googlesource.com/c/chromium/src/+/1545096) and land | 
 | // it or some form of it. | 
 | void RandBytes(void* output, size_t output_length) { | 
 | #if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && !defined(OS_NACL) | 
 |   // We have to call `getrandom` via Linux Syscall Support, rather than through | 
 |   // the libc wrapper, because we might not have an up-to-date libc (e.g. on | 
 |   // some bots). | 
 |   const ssize_t r = HANDLE_EINTR(sys_getrandom(output, output_length, 0)); | 
 |  | 
 |   // Return success only on total success. In case errno == ENOSYS (or any other | 
 |   // error), we'll fall through to reading from urandom below. | 
 |   if (output_length == static_cast<size_t>(r)) { | 
 |     MSAN_UNPOISON(output, output_length); | 
 |     return; | 
 |   } | 
 | #elif defined(OS_MAC) | 
 |   // TODO(crbug.com/995996): Enable this on iOS too, when sys/random.h arrives | 
 |   // in its SDK. | 
 |   if (__builtin_available(macOS 10.12, *)) { | 
 |     if (getentropy(output, output_length) == 0) { | 
 |       return; | 
 |     } | 
 |   } | 
 | #endif | 
 |  | 
 |   // If the OS-specific mechanisms didn't work, fall through to reading from | 
 |   // urandom. | 
 |   // | 
 |   // TODO(crbug.com/995996): When we no longer need to support old Linux | 
 |   // kernels, we can get rid of this /dev/urandom branch altogether. | 
 |   const int urandom_fd = GetUrandomFD(); | 
 |   const bool success = | 
 |       ReadFromFD(urandom_fd, static_cast<char*>(output), output_length); | 
 |   CHECK(success); | 
 | } | 
 |  | 
 | int GetUrandomFD() { | 
 |   static NoDestructor<URandomFd> urandom_fd; | 
 |   return urandom_fd->fd(); | 
 | } | 
 |  | 
 | }  // namespace base |