// 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 #include #include #include #include #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)) && !defined(OS_NACL) #include "third_party/lss/linux_syscall_support.h" #elif defined(OS_MAC) // TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK. // (See below.) #include #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(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(output), output_length); CHECK(success); } int GetUrandomFD() { static NoDestructor urandom_fd; return urandom_fd->fd(); } } // namespace base