blob: 596aafd899cd22e73136bc3d114421f6489934ce [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[email protected]05f9b682008-09-29 22:18:012// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/rand_util.h"
6
[email protected]09e5f47a2009-06-26 10:00:027#include <errno.h>
[email protected]05f9b682008-09-29 22:18:018#include <fcntl.h>
avi9b6f42932015-12-26 22:15:149#include <stddef.h>
10#include <stdint.h>
Egor Pasko75c09cc02022-08-08 18:14:1311#include <sys/syscall.h>
12#include <sys/utsname.h>
[email protected]05f9b682008-09-29 22:18:0113#include <unistd.h>
14
Hans Wennborgc3cffa62020-04-27 10:09:1215#include "base/check.h"
Chris Palmer3b3588b2020-08-28 17:04:2116#include "base/compiler_specific.h"
Egor Pasko75c09cc02022-08-08 18:14:1317#include "base/feature_list.h"
[email protected]e3177dd52014-08-13 20:22:1418#include "base/files/file_util.h"
Egor Pasko75c09cc02022-08-08 18:14:1319#include "base/metrics/histogram_macros.h"
Lei Zhange3e126d782020-01-07 21:36:0020#include "base/no_destructor.h"
mark4cec4942017-02-28 23:56:0021#include "base/posix/eintr_wrapper.h"
Egor Pasko75c09cc02022-08-08 18:14:1322#include "base/time/time.h"
Chris Palmer254cd5f2020-08-12 20:37:0723#include "build/build_config.h"
24
Xiaohan Wang38e4ebb2022-01-19 06:57:4325#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
Chris Palmer3b3588b2020-08-28 17:04:2126#include "third_party/lss/linux_syscall_support.h"
Xiaohan Wang38e4ebb2022-01-19 06:57:4327#elif BUILDFLAG(IS_MAC)
Chris Palmer254cd5f2020-08-12 20:37:0728// TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK.
Chris Palmer3b3588b2020-08-28 17:04:2129// (See below.)
Chris Palmer254cd5f2020-08-12 20:37:0730#include <sys/random.h>
31#endif
[email protected]05f9b682008-09-29 22:18:0132
[email protected]09e5f47a2009-06-26 10:00:0233namespace {
34
Xiaohan Wang38e4ebb2022-01-19 06:57:4335#if BUILDFLAG(IS_AIX)
Chris Palmer3b3588b2020-08-28 17:04:2136// AIX has no 64-bit support for O_CLOEXEC.
37static constexpr int kOpenFlags = O_RDONLY;
38#else
39static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC;
40#endif
41
[email protected]09e5f47a2009-06-26 10:00:0242// We keep the file descriptor for /dev/urandom around so we don't need to
43// reopen it (which is expensive), and since we may not even be able to reopen
44// it if we are later put in a sandbox. This class wraps the file descriptor so
Lei Zhange3e126d782020-01-07 21:36:0045// we can use a static-local variable to handle opening it on the first access.
[email protected]09e5f47a2009-06-26 10:00:0246class URandomFd {
47 public:
Chris Palmer3b3588b2020-08-28 17:04:2148 URandomFd() : fd_(HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) {
49 CHECK(fd_ >= 0) << "Cannot open /dev/urandom";
rayb0088ee52017-04-26 22:35:0850 }
[email protected]09e5f47a2009-06-26 10:00:0251
[email protected]c910c5a2014-01-23 02:14:2852 ~URandomFd() { close(fd_); }
[email protected]09e5f47a2009-06-26 10:00:0253
54 int fd() const { return fd_; }
55
56 private:
[email protected]c910c5a2014-01-23 02:14:2857 const int fd_;
[email protected]09e5f47a2009-06-26 10:00:0258};
59
Egor Pasko75c09cc02022-08-08 18:14:1360#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
61 BUILDFLAG(IS_ANDROID)) && \
62 !BUILDFLAG(IS_NACL)
63// TODO(pasko): Unify reading kernel version numbers in:
64// mojo/core/channel_linux.cc
65// chrome/browser/android/seccomp_support_detector.cc
66void KernelVersionNumbers(int32_t* major_version,
67 int32_t* minor_version,
68 int32_t* bugfix_version) {
69 struct utsname info;
70 if (uname(&info) < 0) {
71 NOTREACHED();
72 *major_version = 0;
73 *minor_version = 0;
74 *bugfix_version = 0;
75 return;
76 }
77 int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
78 bugfix_version);
79 if (num_read < 1)
80 *major_version = 0;
81 if (num_read < 2)
82 *minor_version = 0;
83 if (num_read < 3)
84 *bugfix_version = 0;
85}
86
87bool KernelSupportsGetRandom() {
88 int32_t major = 0;
89 int32_t minor = 0;
90 int32_t bugfix = 0;
91 KernelVersionNumbers(&major, &minor, &bugfix);
92 if (major >= 3 && minor >= 17)
93 return true;
94 return false;
95}
96
97bool GetRandomSyscall(void* output, size_t output_length) {
98 // We have to call `getrandom` via Linux Syscall Support, rather than through
99 // the libc wrapper, because we might not have an up-to-date libc (e.g. on
100 // some bots).
101 const ssize_t r =
102 HANDLE_EINTR(syscall(__NR_getrandom, output, output_length, 0));
103
104 // Return success only on total success. In case errno == ENOSYS (or any other
105 // error), we'll fall through to reading from urandom below.
106 if (output_length == static_cast<size_t>(r)) {
107 MSAN_UNPOISON(output, output_length);
108 return true;
109 }
110 return false;
111}
112#endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
113 // BUILDFLAG(IS_ANDROID)) && !BUILDFLAG(IS_NACL)
114
115#if BUILDFLAG(IS_ANDROID)
116std::atomic<bool> g_use_getrandom;
117
118const base::Feature kUseGetrandomForRandBytes{
119 "UseGetrandomForRandBytes", base::FEATURE_DISABLED_BY_DEFAULT};
120
121bool UseGetrandom() {
122 return g_use_getrandom.load(std::memory_order_relaxed);
123}
124#elif (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
125bool UseGetrandom() {
126 return true;
127}
128#endif
129
[email protected]09e5f47a2009-06-26 10:00:02130} // namespace
131
[email protected]05f9b682008-09-29 22:18:01132namespace base {
133
Egor Pasko75c09cc02022-08-08 18:14:13134#if BUILDFLAG(IS_ANDROID)
135void ConfigureRandBytesFieldTrial() {
136 g_use_getrandom.store(FeatureList::IsEnabled(kUseGetrandomForRandBytes),
137 std::memory_order_relaxed);
138}
139#endif
140
Chris Palmer3b3588b2020-08-28 17:04:21141// NOTE: In an ideal future, all implementations of this function will just
142// wrap BoringSSL's `RAND_bytes`. TODO(crbug.com/995996): Figure out the
143// build/test/performance issues with dcheng's CL
144// (https://chromium-review.googlesource.com/c/chromium/src/+/1545096) and land
145// it or some form of it.
[email protected]c910c5a2014-01-23 02:14:28146void RandBytes(void* output, size_t output_length) {
Egor Pasko75c09cc02022-08-08 18:14:13147#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
148 BUILDFLAG(IS_ANDROID)) && \
149 !BUILDFLAG(IS_NACL)
150 if (UseGetrandom()) {
151 // On Android it is mandatory to check that the kernel _version_ has the
152 // support for a syscall before calling. The same check is made on Linux and
153 // ChromeOS to avoid making a syscall that predictably returns ENOSYS.
154 static const bool kernel_has_support = KernelSupportsGetRandom();
155 if (kernel_has_support && GetRandomSyscall(output, output_length))
156 return;
Chris Palmer3b3588b2020-08-28 17:04:21157 }
Xiaohan Wang38e4ebb2022-01-19 06:57:43158#elif BUILDFLAG(IS_MAC)
Chris Palmer254cd5f2020-08-12 20:37:07159 // TODO(crbug.com/995996): Enable this on iOS too, when sys/random.h arrives
160 // in its SDK.
Avi Drissmanba195b32022-05-19 02:53:34161 if (getentropy(output, output_length) == 0) {
162 return;
Chris Palmer254cd5f2020-08-12 20:37:07163 }
Chris Palmer254cd5f2020-08-12 20:37:07164#endif
165
Chris Palmer3b3588b2020-08-28 17:04:21166 // If the OS-specific mechanisms didn't work, fall through to reading from
167 // urandom.
168 //
169 // TODO(crbug.com/995996): When we no longer need to support old Linux
170 // kernels, we can get rid of this /dev/urandom branch altogether.
Lei Zhange3e126d782020-01-07 21:36:00171 const int urandom_fd = GetUrandomFD();
[email protected]c910c5a2014-01-23 02:14:28172 const bool success =
173 ReadFromFD(urandom_fd, static_cast<char*>(output), output_length);
174 CHECK(success);
175}
176
Lei Zhange3e126d782020-01-07 21:36:00177int GetUrandomFD() {
178 static NoDestructor<URandomFd> urandom_fd;
179 return urandom_fd->fd();
[email protected]1d87fad2010-03-04 20:18:55180}
[email protected]ead8c1f2012-05-30 14:26:13181
182} // namespace base