1 // Copyright 2018 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/memory/platform_shared_memory_region.h"
6
7 #include <lib/zx/vmar.h>
8 #include <zircon/process.h>
9 #include <zircon/rights.h>
10
11 #include "base/bits.h"
12 #include "base/check_op.h"
13 #include "base/fuchsia/fuchsia_logging.h"
14 #include "base/memory/page_size.h"
15
16 namespace base {
17 namespace subtle {
18
19 static constexpr int kNoWriteOrExec =
20 ZX_DEFAULT_VMO_RIGHTS &
21 ~(ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE | ZX_RIGHT_SET_PROPERTY);
22
23 // static
Take(zx::vmo handle,Mode mode,size_t size,const UnguessableToken & guid)24 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
25 zx::vmo handle,
26 Mode mode,
27 size_t size,
28 const UnguessableToken& guid) {
29 if (!handle.is_valid())
30 return {};
31
32 if (size == 0)
33 return {};
34
35 if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
36 return {};
37
38 CHECK(CheckPlatformHandlePermissionsCorrespondToMode(zx::unowned_vmo(handle),
39 mode, size));
40
41 return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
42 }
43
GetPlatformHandle() const44 zx::unowned_vmo PlatformSharedMemoryRegion::GetPlatformHandle() const {
45 return zx::unowned_vmo(handle_);
46 }
47
IsValid() const48 bool PlatformSharedMemoryRegion::IsValid() const {
49 return handle_.is_valid();
50 }
51
Duplicate() const52 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
53 if (!IsValid())
54 return {};
55
56 CHECK_NE(mode_, Mode::kWritable)
57 << "Duplicating a writable shared memory region is prohibited";
58
59 zx::vmo duped_handle;
60 zx_status_t status = handle_.duplicate(ZX_RIGHT_SAME_RIGHTS, &duped_handle);
61 if (status != ZX_OK) {
62 ZX_DLOG(ERROR, status) << "zx_handle_duplicate";
63 return {};
64 }
65
66 return PlatformSharedMemoryRegion(std::move(duped_handle), mode_, size_,
67 guid_);
68 }
69
ConvertToReadOnly()70 bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
71 if (!IsValid())
72 return false;
73
74 CHECK_EQ(mode_, Mode::kWritable)
75 << "Only writable shared memory region can be converted to read-only";
76
77 zx_status_t status = handle_.replace(kNoWriteOrExec, &handle_);
78 if (status != ZX_OK) {
79 ZX_DLOG(ERROR, status) << "zx_handle_replace";
80 return false;
81 }
82
83 mode_ = Mode::kReadOnly;
84 return true;
85 }
86
ConvertToUnsafe()87 bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
88 if (!IsValid())
89 return false;
90
91 CHECK_EQ(mode_, Mode::kWritable)
92 << "Only writable shared memory region can be converted to unsafe";
93
94 mode_ = Mode::kUnsafe;
95 return true;
96 }
97
98 // static
Create(Mode mode,size_t size)99 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
100 size_t size) {
101 if (size == 0)
102 return {};
103
104 // Aligning may overflow so check that the result doesn't decrease.
105 size_t rounded_size = bits::AlignUp(size, GetPageSize());
106 if (rounded_size < size ||
107 rounded_size > static_cast<size_t>(std::numeric_limits<int>::max())) {
108 return {};
109 }
110
111 CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
112 "lead to this region being non-modifiable";
113
114 zx::vmo vmo;
115 zx_status_t status = zx::vmo::create(rounded_size, 0, &vmo);
116 if (status != ZX_OK) {
117 ZX_DLOG(ERROR, status) << "zx_vmo_create";
118 return {};
119 }
120
121 // TODO(crbug.com/991805): Take base::Location from the caller and use it to
122 // generate the name here.
123 constexpr char kVmoName[] = "cr-shared-memory-region";
124 status = vmo.set_property(ZX_PROP_NAME, kVmoName, strlen(kVmoName));
125 ZX_DCHECK(status == ZX_OK, status);
126
127 const int kNoExecFlags = ZX_DEFAULT_VMO_RIGHTS & ~ZX_RIGHT_EXECUTE;
128 status = vmo.replace(kNoExecFlags, &vmo);
129 if (status != ZX_OK) {
130 ZX_DLOG(ERROR, status) << "zx_handle_replace";
131 return {};
132 }
133
134 return PlatformSharedMemoryRegion(std::move(vmo), mode, size,
135 UnguessableToken::Create());
136 }
137
138 // static
CheckPlatformHandlePermissionsCorrespondToMode(PlatformSharedMemoryHandle handle,Mode mode,size_t size)139 bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
140 PlatformSharedMemoryHandle handle,
141 Mode mode,
142 size_t size) {
143 zx_info_handle_basic_t basic = {};
144 zx_status_t status = handle->get_info(ZX_INFO_HANDLE_BASIC, &basic,
145 sizeof(basic), nullptr, nullptr);
146 ZX_CHECK(status == ZX_OK, status) << "zx_object_get_info";
147
148 if (basic.type != ZX_OBJ_TYPE_VMO) {
149 // TODO(crbug.com/838365): convert to DLOG when bug fixed.
150 LOG(ERROR) << "Received zircon handle is not a VMO";
151 return false;
152 }
153
154 bool is_read_only = (basic.rights & (ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE)) == 0;
155 bool expected_read_only = mode == Mode::kReadOnly;
156
157 if (is_read_only != expected_read_only) {
158 // TODO(crbug.com/838365): convert to DLOG when bug fixed.
159 LOG(ERROR) << "VMO object has wrong access rights: it is"
160 << (is_read_only ? " " : " not ") << "read-only but it should"
161 << (expected_read_only ? " " : " not ") << "be";
162 return false;
163 }
164
165 return true;
166 }
167
PlatformSharedMemoryRegion(zx::vmo handle,Mode mode,size_t size,const UnguessableToken & guid)168 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
169 zx::vmo handle,
170 Mode mode,
171 size_t size,
172 const UnguessableToken& guid)
173 : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
174
175 } // namespace subtle
176 } // namespace base
177