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