123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- // 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 <sys/syscall.h>
- #include <sys/utsname.h>
- #include <unistd.h>
- #include "base/check.h"
- #include "base/compiler_specific.h"
- #include "base/feature_list.h"
- #include "base/files/file_util.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/no_destructor.h"
- #include "base/posix/eintr_wrapper.h"
- #include "base/time/time.h"
- #include "build/build_config.h"
- #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
- #include "third_party/lss/linux_syscall_support.h"
- #elif BUILDFLAG(IS_MAC)
- // TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK.
- // (See below.)
- #include <sys/random.h>
- #endif
- namespace {
- #if BUILDFLAG(IS_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_;
- };
- #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
- BUILDFLAG(IS_ANDROID)) && \
- !BUILDFLAG(IS_NACL)
- // TODO(pasko): Unify reading kernel version numbers in:
- // mojo/core/channel_linux.cc
- // chrome/browser/android/seccomp_support_detector.cc
- void KernelVersionNumbers(int32_t* major_version,
- int32_t* minor_version,
- int32_t* bugfix_version) {
- struct utsname info;
- if (uname(&info) < 0) {
- NOTREACHED();
- *major_version = 0;
- *minor_version = 0;
- *bugfix_version = 0;
- return;
- }
- int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
- bugfix_version);
- if (num_read < 1)
- *major_version = 0;
- if (num_read < 2)
- *minor_version = 0;
- if (num_read < 3)
- *bugfix_version = 0;
- }
- bool KernelSupportsGetRandom() {
- int32_t major = 0;
- int32_t minor = 0;
- int32_t bugfix = 0;
- KernelVersionNumbers(&major, &minor, &bugfix);
- if (major >= 3 && minor >= 17)
- return true;
- return false;
- }
- bool GetRandomSyscall(void* output, size_t output_length) {
- // 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(syscall(__NR_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 true;
- }
- return false;
- }
- #endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
- // BUILDFLAG(IS_ANDROID)) && !BUILDFLAG(IS_NACL)
- #if BUILDFLAG(IS_ANDROID)
- std::atomic<bool> g_use_getrandom;
- const base::Feature kUseGetrandomForRandBytes{
- "UseGetrandomForRandBytes", base::FEATURE_DISABLED_BY_DEFAULT};
- bool UseGetrandom() {
- return g_use_getrandom.load(std::memory_order_relaxed);
- }
- #elif (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
- bool UseGetrandom() {
- return true;
- }
- #endif
- } // namespace
- namespace base {
- #if BUILDFLAG(IS_ANDROID)
- void ConfigureRandBytesFieldTrial() {
- g_use_getrandom.store(FeatureList::IsEnabled(kUseGetrandomForRandBytes),
- std::memory_order_relaxed);
- }
- #endif
- // 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 (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
- BUILDFLAG(IS_ANDROID)) && \
- !BUILDFLAG(IS_NACL)
- if (UseGetrandom()) {
- // On Android it is mandatory to check that the kernel _version_ has the
- // support for a syscall before calling. The same check is made on Linux and
- // ChromeOS to avoid making a syscall that predictably returns ENOSYS.
- static const bool kernel_has_support = KernelSupportsGetRandom();
- if (kernel_has_support && GetRandomSyscall(output, output_length))
- return;
- }
- #elif BUILDFLAG(IS_MAC)
- // TODO(crbug.com/995996): Enable this on iOS too, when sys/random.h arrives
- // in its SDK.
- 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
|