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 "partition_alloc/partition_alloc_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 <unistd.h>
13 #include <sstream>
14
15 #include "build/build_config.h"
16 #include "partition_alloc/partition_alloc_base/check.h"
17 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
18 #include "partition_alloc/partition_alloc_base/files/file_util.h"
19 #include "partition_alloc/partition_alloc_base/no_destructor.h"
20 #include "partition_alloc/partition_alloc_base/posix/eintr_wrapper.h"
21
22 #if BUILDFLAG(IS_MAC)
23 // TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK.
24 // (See below.)
25 #include <sys/random.h>
26 #endif
27
28 namespace {
29
30 #if BUILDFLAG(IS_AIX)
31 // AIX has no 64-bit support for O_CLOEXEC.
32 static constexpr int kOpenFlags = O_RDONLY;
33 #else
34 static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC;
35 #endif
36
37 // We keep the file descriptor for /dev/urandom around so we don't need to
38 // reopen it (which is expensive), and since we may not even be able to reopen
39 // it if we are later put in a sandbox. This class wraps the file descriptor so
40 // we can use a static-local variable to handle opening it on the first access.
41 class URandomFd {
42 public:
URandomFd()43 URandomFd() : fd_(PA_HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) {
44 PA_BASE_CHECK(fd_ >= 0) << "Cannot open /dev/urandom";
45 }
46
~URandomFd()47 ~URandomFd() { close(fd_); }
48
fd() const49 int fd() const { return fd_; }
50
51 private:
52 const int fd_;
53 };
54
GetUrandomFD()55 int GetUrandomFD() {
56 static partition_alloc::internal::base::NoDestructor<URandomFd> urandom_fd;
57 return urandom_fd->fd();
58 }
59
60 } // namespace
61
62 namespace partition_alloc::internal::base {
63
64 // NOTE: In an ideal future, all implementations of this function will just
65 // wrap BoringSSL's `RAND_bytes`. TODO(crbug.com/995996): Figure out the
66 // build/test/performance issues with dcheng's CL
67 // (https://chromium-review.googlesource.com/c/chromium/src/+/1545096) and land
68 // it or some form of it.
RandBytes(void * output,size_t output_length)69 void RandBytes(void* output, size_t output_length) {
70 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
71 // Use `syscall(__NR_getrandom...` to avoid a dependency on
72 // `third_party/linux_syscall_support.h`.
73 //
74 // Here in PartitionAlloc, we don't need to look before we leap
75 // because we know that both Linux and CrOS only support kernels
76 // that do have this syscall defined. This diverges from upstream
77 // `//base` behavior both here and below.
78 const ssize_t r =
79 PA_HANDLE_EINTR(syscall(__NR_getrandom, output, output_length, 0));
80
81 // Return success only on total success. In case errno == ENOSYS (or any other
82 // error), we'll fall through to reading from urandom below.
83 if (output_length == static_cast<size_t>(r)) {
84 PA_MSAN_UNPOISON(output, output_length);
85 return;
86 }
87 #elif BUILDFLAG(IS_MAC)
88 // TODO(crbug.com/995996): Enable this on iOS too, when sys/random.h arrives
89 // in its SDK.
90 if (getentropy(output, output_length) == 0) {
91 return;
92 }
93 #endif
94 // If getrandom(2) above returned with an error and the /dev/urandom fallback
95 // took place on Linux/ChromeOS bots, they would fail with a CHECK in
96 // nacl_helper. The latter assumes that the number of open file descriptors
97 // must be constant. The nacl_helper knows about the FD from
98 // //base/rand_utils, but is not aware of the urandom_fd from this file (see
99 // CheckForExpectedNumberOfOpenFds).
100 //
101 // * On `linux_chromium_asan_rel_ng` in
102 // `ContentBrowserTest.RendererCrashCallStack`:
103 // ```
104 // [FATAL:rand_util_posix.cc(45)] Check failed: fd_ >= 0. Cannot open
105 // /dev/urandom
106 // ```
107 // * On `linux-lacros-rel` in
108 // `NaClBrowserTestGLibc.CrashInCallback`:
109 // ```
110 // 2023-07-03T11:31:13.115755Z FATAL nacl_helper:
111 // [nacl_sandbox_linux.cc(178)] Check failed: expected_num_fds ==
112 // sandbox::ProcUtil::CountOpenFds(proc_fd_.get()) (6 vs. 7)
113 // ```
114 const int urandom_fd = GetUrandomFD();
115 const bool success =
116 ReadFromFD(urandom_fd, static_cast<char*>(output), output_length);
117 PA_BASE_CHECK(success);
118 }
119
120 } // namespace partition_alloc::internal::base
121