rand_util_posix.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/rand_util.h"
  5. #include <errno.h>
  6. #include <fcntl.h>
  7. #include <stddef.h>
  8. #include <stdint.h>
  9. #include <sys/syscall.h>
  10. #include <sys/utsname.h>
  11. #include <unistd.h>
  12. #include "base/check.h"
  13. #include "base/compiler_specific.h"
  14. #include "base/feature_list.h"
  15. #include "base/files/file_util.h"
  16. #include "base/metrics/histogram_macros.h"
  17. #include "base/no_destructor.h"
  18. #include "base/posix/eintr_wrapper.h"
  19. #include "base/time/time.h"
  20. #include "build/build_config.h"
  21. #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
  22. #include "third_party/lss/linux_syscall_support.h"
  23. #elif BUILDFLAG(IS_MAC)
  24. // TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK.
  25. // (See below.)
  26. #include <sys/random.h>
  27. #endif
  28. namespace {
  29. #if BUILDFLAG(IS_AIX)
  30. // AIX has no 64-bit support for O_CLOEXEC.
  31. static constexpr int kOpenFlags = O_RDONLY;
  32. #else
  33. static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC;
  34. #endif
  35. // We keep the file descriptor for /dev/urandom around so we don't need to
  36. // reopen it (which is expensive), and since we may not even be able to reopen
  37. // it if we are later put in a sandbox. This class wraps the file descriptor so
  38. // we can use a static-local variable to handle opening it on the first access.
  39. class URandomFd {
  40. public:
  41. URandomFd() : fd_(HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) {
  42. CHECK(fd_ >= 0) << "Cannot open /dev/urandom";
  43. }
  44. ~URandomFd() { close(fd_); }
  45. int fd() const { return fd_; }
  46. private:
  47. const int fd_;
  48. };
  49. #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
  50. BUILDFLAG(IS_ANDROID)) && \
  51. !BUILDFLAG(IS_NACL)
  52. // TODO(pasko): Unify reading kernel version numbers in:
  53. // mojo/core/channel_linux.cc
  54. // chrome/browser/android/seccomp_support_detector.cc
  55. void KernelVersionNumbers(int32_t* major_version,
  56. int32_t* minor_version,
  57. int32_t* bugfix_version) {
  58. struct utsname info;
  59. if (uname(&info) < 0) {
  60. NOTREACHED();
  61. *major_version = 0;
  62. *minor_version = 0;
  63. *bugfix_version = 0;
  64. return;
  65. }
  66. int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
  67. bugfix_version);
  68. if (num_read < 1)
  69. *major_version = 0;
  70. if (num_read < 2)
  71. *minor_version = 0;
  72. if (num_read < 3)
  73. *bugfix_version = 0;
  74. }
  75. bool KernelSupportsGetRandom() {
  76. int32_t major = 0;
  77. int32_t minor = 0;
  78. int32_t bugfix = 0;
  79. KernelVersionNumbers(&major, &minor, &bugfix);
  80. if (major >= 3 && minor >= 17)
  81. return true;
  82. return false;
  83. }
  84. bool GetRandomSyscall(void* output, size_t output_length) {
  85. // We have to call `getrandom` via Linux Syscall Support, rather than through
  86. // the libc wrapper, because we might not have an up-to-date libc (e.g. on
  87. // some bots).
  88. const ssize_t r =
  89. HANDLE_EINTR(syscall(__NR_getrandom, output, output_length, 0));
  90. // Return success only on total success. In case errno == ENOSYS (or any other
  91. // error), we'll fall through to reading from urandom below.
  92. if (output_length == static_cast<size_t>(r)) {
  93. MSAN_UNPOISON(output, output_length);
  94. return true;
  95. }
  96. return false;
  97. }
  98. #endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
  99. // BUILDFLAG(IS_ANDROID)) && !BUILDFLAG(IS_NACL)
  100. #if BUILDFLAG(IS_ANDROID)
  101. std::atomic<bool> g_use_getrandom;
  102. const base::Feature kUseGetrandomForRandBytes{
  103. "UseGetrandomForRandBytes", base::FEATURE_DISABLED_BY_DEFAULT};
  104. bool UseGetrandom() {
  105. return g_use_getrandom.load(std::memory_order_relaxed);
  106. }
  107. #elif (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
  108. bool UseGetrandom() {
  109. return true;
  110. }
  111. #endif
  112. } // namespace
  113. namespace base {
  114. #if BUILDFLAG(IS_ANDROID)
  115. void ConfigureRandBytesFieldTrial() {
  116. g_use_getrandom.store(FeatureList::IsEnabled(kUseGetrandomForRandBytes),
  117. std::memory_order_relaxed);
  118. }
  119. #endif
  120. // NOTE: In an ideal future, all implementations of this function will just
  121. // wrap BoringSSL's `RAND_bytes`. TODO(crbug.com/995996): Figure out the
  122. // build/test/performance issues with dcheng's CL
  123. // (https://chromium-review.googlesource.com/c/chromium/src/+/1545096) and land
  124. // it or some form of it.
  125. void RandBytes(void* output, size_t output_length) {
  126. #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
  127. BUILDFLAG(IS_ANDROID)) && \
  128. !BUILDFLAG(IS_NACL)
  129. if (UseGetrandom()) {
  130. // On Android it is mandatory to check that the kernel _version_ has the
  131. // support for a syscall before calling. The same check is made on Linux and
  132. // ChromeOS to avoid making a syscall that predictably returns ENOSYS.
  133. static const bool kernel_has_support = KernelSupportsGetRandom();
  134. if (kernel_has_support && GetRandomSyscall(output, output_length))
  135. return;
  136. }
  137. #elif BUILDFLAG(IS_MAC)
  138. // TODO(crbug.com/995996): Enable this on iOS too, when sys/random.h arrives
  139. // in its SDK.
  140. if (getentropy(output, output_length) == 0) {
  141. return;
  142. }
  143. #endif
  144. // If the OS-specific mechanisms didn't work, fall through to reading from
  145. // urandom.
  146. //
  147. // TODO(crbug.com/995996): When we no longer need to support old Linux
  148. // kernels, we can get rid of this /dev/urandom branch altogether.
  149. const int urandom_fd = GetUrandomFD();
  150. const bool success =
  151. ReadFromFD(urandom_fd, static_cast<char*>(output), output_length);
  152. CHECK(success);
  153. }
  154. int GetUrandomFD() {
  155. static NoDestructor<URandomFd> urandom_fd;
  156. return urandom_fd->fd();
  157. }
  158. } // namespace base