xref: /aosp_15_r20/external/cronet/base/rand_util_posix.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 #include "base/rand_util.h"
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <sys/syscall.h>
12 #include <sys/utsname.h>
13 #include <unistd.h>
14 
15 #include "base/check.h"
16 #include "base/compiler_specific.h"
17 #include "base/containers/span.h"
18 #include "base/feature_list.h"
19 #include "base/files/file_util.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/no_destructor.h"
22 #include "base/posix/eintr_wrapper.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 
26 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
27 #include "third_party/lss/linux_syscall_support.h"
28 #elif BUILDFLAG(IS_MAC)
29 // TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK.
30 // (See below.)
31 #include <sys/random.h>
32 #endif
33 
34 #if !BUILDFLAG(IS_NACL)
35 #include "third_party/boringssl/src/include/openssl/crypto.h"
36 #include "third_party/boringssl/src/include/openssl/rand.h"
37 #endif
38 
39 namespace base {
40 
41 namespace {
42 
43 #if BUILDFLAG(IS_AIX)
44 // AIX has no 64-bit support for O_CLOEXEC.
45 static constexpr int kOpenFlags = O_RDONLY;
46 #else
47 static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC;
48 #endif
49 
50 // We keep the file descriptor for /dev/urandom around so we don't need to
51 // reopen it (which is expensive), and since we may not even be able to reopen
52 // it if we are later put in a sandbox. This class wraps the file descriptor so
53 // we can use a static-local variable to handle opening it on the first access.
54 class URandomFd {
55  public:
URandomFd()56   URandomFd() : fd_(HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) {
57     CHECK(fd_ >= 0) << "Cannot open /dev/urandom";
58   }
59 
~URandomFd()60   ~URandomFd() { close(fd_); }
61 
fd() const62   int fd() const { return fd_; }
63 
64  private:
65   const int fd_;
66 };
67 
68 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
69      BUILDFLAG(IS_ANDROID)) &&                        \
70     !BUILDFLAG(IS_NACL)
71 // TODO(pasko): Unify reading kernel version numbers in:
72 // mojo/core/channel_linux.cc
73 // chrome/browser/android/seccomp_support_detector.cc
KernelVersionNumbers(int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)74 void KernelVersionNumbers(int32_t* major_version,
75                           int32_t* minor_version,
76                           int32_t* bugfix_version) {
77   struct utsname info;
78   if (uname(&info) < 0) {
79     NOTREACHED();
80     *major_version = 0;
81     *minor_version = 0;
82     *bugfix_version = 0;
83     return;
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 #if BUILDFLAG(IS_ANDROID)
124 std::atomic<bool> g_use_getrandom;
125 
126 // Note: the BoringSSL feature takes precedence over the getrandom() trial if
127 // both are enabled.
128 BASE_FEATURE(kUseGetrandomForRandBytes,
129              "UseGetrandomForRandBytes",
130              FEATURE_ENABLED_BY_DEFAULT);
131 
UseGetrandom()132 bool UseGetrandom() {
133   return g_use_getrandom.load(std::memory_order_relaxed);
134 }
135 #elif (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_NACL)
UseGetrandom()136 bool UseGetrandom() {
137   return true;
138 }
139 #endif
140 
141 }  // namespace
142 
143 namespace internal {
144 
145 #if BUILDFLAG(IS_ANDROID)
ConfigureRandBytesFieldTrial()146 void ConfigureRandBytesFieldTrial() {
147   g_use_getrandom.store(FeatureList::IsEnabled(kUseGetrandomForRandBytes),
148                         std::memory_order_relaxed);
149 }
150 #endif
151 
152 namespace {
153 
154 #if !BUILDFLAG(IS_NACL)
155 // The BoringSSl helpers are duplicated in rand_util_fuchsia.cc and
156 // rand_util_win.cc.
157 std::atomic<bool> g_use_boringssl;
158 
159 BASE_FEATURE(kUseBoringSSLForRandBytes,
160              "UseBoringSSLForRandBytes",
161              FEATURE_DISABLED_BY_DEFAULT);
162 
163 }  // namespace
164 
ConfigureBoringSSLBackedRandBytesFieldTrial()165 void ConfigureBoringSSLBackedRandBytesFieldTrial() {
166   g_use_boringssl.store(FeatureList::IsEnabled(kUseBoringSSLForRandBytes),
167                         std::memory_order_relaxed);
168 }
169 
UseBoringSSLForRandBytes()170 bool UseBoringSSLForRandBytes() {
171   return g_use_boringssl.load(std::memory_order_relaxed);
172 }
173 #endif
174 
175 }  // namespace internal
176 
177 namespace {
178 
RandBytes(span<uint8_t> output,bool avoid_allocation)179 void RandBytes(span<uint8_t> output, bool avoid_allocation) {
180 #if !BUILDFLAG(IS_NACL)
181   // The BoringSSL experiment takes priority over everything else.
182   if (!avoid_allocation && internal::UseBoringSSLForRandBytes()) {
183     // Ensure BoringSSL is initialized so it can use things like RDRAND.
184     CRYPTO_library_init();
185     // BoringSSL's RAND_bytes always returns 1. Any error aborts the program.
186     (void)RAND_bytes(output.data(), output.size());
187     return;
188   }
189 #endif
190 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
191      BUILDFLAG(IS_ANDROID)) &&                        \
192     !BUILDFLAG(IS_NACL)
193   if (avoid_allocation || UseGetrandom()) {
194     // On Android it is mandatory to check that the kernel _version_ has the
195     // support for a syscall before calling. The same check is made on Linux and
196     // ChromeOS to avoid making a syscall that predictably returns ENOSYS.
197     static const bool kernel_has_support = KernelSupportsGetRandom();
198     if (kernel_has_support && GetRandomSyscall(output.data(), output.size())) {
199       return;
200     }
201   }
202 #elif BUILDFLAG(IS_MAC)
203   // TODO(crbug.com/995996): Enable this on iOS too, when sys/random.h arrives
204   // in its SDK.
205   if (getentropy(output.data(), output.size()) == 0) {
206     return;
207   }
208 #endif
209 
210   // If the OS-specific mechanisms didn't work, fall through to reading from
211   // urandom.
212   //
213   // TODO(crbug.com/995996): When we no longer need to support old Linux
214   // kernels, we can get rid of this /dev/urandom branch altogether.
215   const int urandom_fd = GetUrandomFD();
216   const bool success = ReadFromFD(urandom_fd, as_writable_chars(output));
217   CHECK(success);
218 }
219 
220 }  // namespace
221 
222 namespace internal {
223 
RandDoubleAvoidAllocation()224 double RandDoubleAvoidAllocation() {
225   uint64_t number;
226   RandBytes(as_writable_bytes(make_span(&number, 1u)),
227             /*avoid_allocation=*/true);
228   // This transformation is explained in rand_util.cc.
229   return (number >> 11) * 0x1.0p-53;
230 }
231 
232 }  // namespace internal
233 
RandBytes(span<uint8_t> output)234 void RandBytes(span<uint8_t> output) {
235   RandBytes(output, /*avoid_allocation=*/false);
236 }
237 
RandBytes(void * output,size_t output_length)238 void RandBytes(void* output, size_t output_length) {
239   RandBytes(make_span(static_cast<uint8_t*>(output), output_length));
240 }
241 
GetUrandomFD()242 int GetUrandomFD() {
243   static NoDestructor<URandomFd> urandom_fd;
244   return urandom_fd->fd();
245 }
246 
247 }  // namespace base
248