• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/rand_util.h"
11 
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 #include <sys/syscall.h>
17 #include <sys/utsname.h>
18 #include <unistd.h>
19 
20 #include "base/check.h"
21 #include "base/compiler_specific.h"
22 #include "base/containers/span.h"
23 #include "base/feature_list.h"
24 #include "base/files/file_util.h"
25 #include "base/metrics/histogram_macros.h"
26 #include "base/no_destructor.h"
27 #include "base/posix/eintr_wrapper.h"
28 #include "base/time/time.h"
29 #include "build/build_config.h"
30 
31 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
32 #include "third_party/lss/linux_syscall_support.h"
33 #elif BUILDFLAG(IS_MAC)
34 // TODO(crbug.com/40641285): Waiting for this header to appear in the iOS SDK.
35 // (See below.)
36 #include <sys/random.h>
37 #endif
38 
39 #if !BUILDFLAG(IS_NACL)
40 #include "third_party/boringssl/src/include/openssl/rand.h"
41 #endif
42 
43 namespace base {
44 
45 namespace {
46 
47 #if BUILDFLAG(IS_AIX)
48 // AIX has no 64-bit support for O_CLOEXEC.
49 static constexpr int kOpenFlags = O_RDONLY;
50 #else
51 static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC;
52 #endif
53 
54 // We keep the file descriptor for /dev/urandom around so we don't need to
55 // reopen it (which is expensive), and since we may not even be able to reopen
56 // it if we are later put in a sandbox. This class wraps the file descriptor so
57 // we can use a static-local variable to handle opening it on the first access.
58 class URandomFd {
59  public:
URandomFd()60   URandomFd() : fd_(HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) {
61     CHECK(fd_ >= 0) << "Cannot open /dev/urandom";
62   }
63 
~URandomFd()64   ~URandomFd() { close(fd_); }
65 
fd() const66   int fd() const { return fd_; }
67 
68  private:
69   const int fd_;
70 };
71 
72 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
73      BUILDFLAG(IS_ANDROID)) &&                        \
74     !BUILDFLAG(IS_NACL)
75 // TODO(pasko): Unify reading kernel version numbers in:
76 // mojo/core/channel_linux.cc
77 // chrome/browser/android/seccomp_support_detector.cc
KernelVersionNumbers(int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)78 void KernelVersionNumbers(int32_t* major_version,
79                           int32_t* minor_version,
80                           int32_t* bugfix_version) {
81   struct utsname info;
82   if (uname(&info) < 0) {
83     NOTREACHED();
84   }
85   int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
86                         bugfix_version);
87   if (num_read < 1)
88     *major_version = 0;
89   if (num_read < 2)
90     *minor_version = 0;
91   if (num_read < 3)
92     *bugfix_version = 0;
93 }
94 
KernelSupportsGetRandom()95 bool KernelSupportsGetRandom() {
96   int32_t major = 0;
97   int32_t minor = 0;
98   int32_t bugfix = 0;
99   KernelVersionNumbers(&major, &minor, &bugfix);
100   if (major > 3 || (major == 3 && minor >= 17))
101     return true;
102   return false;
103 }
104 
GetRandomSyscall(void * output,size_t output_length)105 bool GetRandomSyscall(void* output, size_t output_length) {
106   // We have to call `getrandom` via Linux Syscall Support, rather than through
107   // the libc wrapper, because we might not have an up-to-date libc (e.g. on
108   // some bots).
109   const ssize_t r =
110       HANDLE_EINTR(syscall(__NR_getrandom, output, output_length, 0));
111 
112   // Return success only on total success. In case errno == ENOSYS (or any other
113   // error), we'll fall through to reading from urandom below.
114   if (output_length == static_cast<size_t>(r)) {
115     MSAN_UNPOISON(output, output_length);
116     return true;
117   }
118   return false;
119 }
120 #endif  // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
121         // BUILDFLAG(IS_ANDROID)) && !BUILDFLAG(IS_NACL)
122 
123 }  // namespace
124 
125 namespace internal {
126 
127 namespace {
128 
129 #if !BUILDFLAG(IS_NACL)
130 // The BoringSSl helpers are duplicated in rand_util_fuchsia.cc and
131 // rand_util_win.cc.
132 std::atomic<bool> g_use_boringssl;
133 
134 BASE_FEATURE(kUseBoringSSLForRandBytes,
135              "UseBoringSSLForRandBytes",
136              FEATURE_DISABLED_BY_DEFAULT);
137 
138 }  // namespace
139 
ConfigureBoringSSLBackedRandBytesFieldTrial()140 void ConfigureBoringSSLBackedRandBytesFieldTrial() {
141   g_use_boringssl.store(FeatureList::IsEnabled(kUseBoringSSLForRandBytes),
142                         std::memory_order_relaxed);
143 }
144 
UseBoringSSLForRandBytes()145 bool UseBoringSSLForRandBytes() {
146   return g_use_boringssl.load(std::memory_order_relaxed);
147 }
148 #endif
149 
150 }  // namespace internal
151 
152 namespace {
153 
RandBytesInternal(span<uint8_t> output,bool avoid_allocation)154 void RandBytesInternal(span<uint8_t> output, bool avoid_allocation) {
155 #if !BUILDFLAG(IS_NACL)
156   // The BoringSSL experiment takes priority over everything else.
157   if (!avoid_allocation && internal::UseBoringSSLForRandBytes()) {
158     // BoringSSL's RAND_bytes always returns 1. Any error aborts the program.
159     (void)RAND_bytes(output.data(), output.size());
160     return;
161   }
162 #endif
163 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
164      BUILDFLAG(IS_ANDROID)) &&                        \
165     !BUILDFLAG(IS_NACL)
166   // On Android it is mandatory to check that the kernel _version_ has the
167   // support for a syscall before calling. The same check is made on Linux and
168   // ChromeOS to avoid making a syscall that predictably returns ENOSYS.
169   static const bool kernel_has_support = KernelSupportsGetRandom();
170   if (kernel_has_support && GetRandomSyscall(output.data(), output.size())) {
171     return;
172   }
173 #elif BUILDFLAG(IS_MAC)
174   // TODO(crbug.com/40641285): Enable this on iOS too, when sys/random.h arrives
175   // in its SDK.
176   if (getentropy(output.data(), output.size()) == 0) {
177     return;
178   }
179 #endif
180 
181   // If the OS-specific mechanisms didn't work, fall through to reading from
182   // urandom.
183   //
184   // TODO(crbug.com/40641285): When we no longer need to support old Linux
185   // kernels, we can get rid of this /dev/urandom branch altogether.
186   const int urandom_fd = GetUrandomFD();
187   const bool success = ReadFromFD(urandom_fd, as_writable_chars(output));
188   CHECK(success);
189 }
190 
191 }  // namespace
192 
193 namespace internal {
194 
RandDoubleAvoidAllocation()195 double RandDoubleAvoidAllocation() {
196   uint64_t number;
197   RandBytesInternal(byte_span_from_ref(number), /*avoid_allocation=*/true);
198   // This transformation is explained in rand_util.cc.
199   return (number >> 11) * 0x1.0p-53;
200 }
201 
202 }  // namespace internal
203 
RandBytes(span<uint8_t> output)204 void RandBytes(span<uint8_t> output) {
205   RandBytesInternal(output, /*avoid_allocation=*/false);
206 }
207 
GetUrandomFD()208 int GetUrandomFD() {
209   static NoDestructor<URandomFd> urandom_fd;
210   return urandom_fd->fd();
211 }
212 
213 }  // namespace base
214