Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/compiler-rt/lib/scudo/standalone/mem_map_fuchsia.cpp
35292 views
1
//===-- mem_map_fuchsia.cpp -------------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "mem_map_fuchsia.h"
10
11
#include "atomic_helpers.h"
12
#include "common.h"
13
#include "string_utils.h"
14
15
#if SCUDO_FUCHSIA
16
17
#include <zircon/process.h>
18
#include <zircon/status.h>
19
#include <zircon/syscalls.h>
20
21
namespace scudo {
22
23
static void NORETURN dieOnError(zx_status_t Status, const char *FnName,
24
uptr Size) {
25
ScopedString Error;
26
Error.append("SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,
27
Size >> 10, _zx_status_get_string(Status));
28
outputRaw(Error.data());
29
die();
30
}
31
32
static void setVmoName(zx_handle_t Vmo, const char *Name) {
33
size_t Len = strlen(Name);
34
DCHECK_LT(Len, ZX_MAX_NAME_LEN);
35
zx_status_t Status = _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, Len);
36
CHECK_EQ(Status, ZX_OK);
37
}
38
39
// Returns the (cached) base address of the root VMAR.
40
static uptr getRootVmarBase() {
41
static atomic_uptr CachedResult = {0};
42
43
uptr Result = atomic_load(&CachedResult, memory_order_acquire);
44
if (UNLIKELY(!Result)) {
45
zx_info_vmar_t VmarInfo;
46
zx_status_t Status =
47
_zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &VmarInfo,
48
sizeof(VmarInfo), nullptr, nullptr);
49
CHECK_EQ(Status, ZX_OK);
50
CHECK_NE(VmarInfo.base, 0);
51
52
atomic_store(&CachedResult, VmarInfo.base, memory_order_release);
53
Result = VmarInfo.base;
54
}
55
56
return Result;
57
}
58
59
// Lazily creates and then always returns the same zero-sized VMO.
60
static zx_handle_t getPlaceholderVmo() {
61
static atomic_u32 StoredVmo = {ZX_HANDLE_INVALID};
62
63
zx_handle_t Vmo = atomic_load(&StoredVmo, memory_order_acquire);
64
if (UNLIKELY(Vmo == ZX_HANDLE_INVALID)) {
65
// Create a zero-sized placeholder VMO.
66
zx_status_t Status = _zx_vmo_create(0, 0, &Vmo);
67
if (UNLIKELY(Status != ZX_OK))
68
dieOnError(Status, "zx_vmo_create", 0);
69
70
setVmoName(Vmo, "scudo:reserved");
71
72
// Atomically store its handle. If some other thread wins the race, use its
73
// handle and discard ours.
74
zx_handle_t OldValue = atomic_compare_exchange_strong(
75
&StoredVmo, ZX_HANDLE_INVALID, Vmo, memory_order_acq_rel);
76
if (UNLIKELY(OldValue != ZX_HANDLE_INVALID)) {
77
Status = _zx_handle_close(Vmo);
78
CHECK_EQ(Status, ZX_OK);
79
80
Vmo = OldValue;
81
}
82
}
83
84
return Vmo;
85
}
86
87
// Checks if MAP_ALLOWNOMEM allows the given error code.
88
static bool IsNoMemError(zx_status_t Status) {
89
// Note: _zx_vmar_map returns ZX_ERR_NO_RESOURCES if the VMAR does not contain
90
// a suitable free spot.
91
return Status == ZX_ERR_NO_MEMORY || Status == ZX_ERR_NO_RESOURCES;
92
}
93
94
// Note: this constructor is only called by ReservedMemoryFuchsia::dispatch.
95
MemMapFuchsia::MemMapFuchsia(uptr Base, uptr Capacity)
96
: MapAddr(Base), WindowBase(Base), WindowSize(Capacity) {
97
// Create the VMO.
98
zx_status_t Status = _zx_vmo_create(Capacity, 0, &Vmo);
99
if (UNLIKELY(Status != ZX_OK))
100
dieOnError(Status, "zx_vmo_create", Capacity);
101
102
setVmoName(Vmo, "scudo:dispatched");
103
}
104
105
bool MemMapFuchsia::mapImpl(UNUSED uptr Addr, uptr Size, const char *Name,
106
uptr Flags) {
107
const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
108
const bool PreCommit = !!(Flags & MAP_PRECOMMIT);
109
const bool NoAccess = !!(Flags & MAP_NOACCESS);
110
111
// Create the VMO.
112
zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);
113
if (UNLIKELY(Status != ZX_OK)) {
114
if (AllowNoMem && IsNoMemError(Status))
115
return false;
116
dieOnError(Status, "zx_vmo_create", Size);
117
}
118
119
if (Name != nullptr)
120
setVmoName(Vmo, Name);
121
122
// Map it.
123
zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS;
124
if (!NoAccess)
125
MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
126
Status =
127
_zx_vmar_map(_zx_vmar_root_self(), MapFlags, 0, Vmo, 0, Size, &MapAddr);
128
if (UNLIKELY(Status != ZX_OK)) {
129
if (AllowNoMem && IsNoMemError(Status)) {
130
Status = _zx_handle_close(Vmo);
131
CHECK_EQ(Status, ZX_OK);
132
133
MapAddr = 0;
134
Vmo = ZX_HANDLE_INVALID;
135
return false;
136
}
137
dieOnError(Status, "zx_vmar_map", Size);
138
}
139
140
if (PreCommit) {
141
Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,
142
Size, nullptr, 0);
143
CHECK_EQ(Status, ZX_OK);
144
}
145
146
WindowBase = MapAddr;
147
WindowSize = Size;
148
return true;
149
}
150
151
void MemMapFuchsia::unmapImpl(uptr Addr, uptr Size) {
152
zx_status_t Status;
153
154
if (Size == WindowSize) {
155
// NOTE: Closing first and then unmapping seems slightly faster than doing
156
// the same operations in the opposite order.
157
Status = _zx_handle_close(Vmo);
158
CHECK_EQ(Status, ZX_OK);
159
Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);
160
CHECK_EQ(Status, ZX_OK);
161
162
MapAddr = WindowBase = WindowSize = 0;
163
Vmo = ZX_HANDLE_INVALID;
164
} else {
165
// Unmap the subrange.
166
Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);
167
CHECK_EQ(Status, ZX_OK);
168
169
// Decommit the pages that we just unmapped.
170
Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, Addr - MapAddr, Size,
171
nullptr, 0);
172
CHECK_EQ(Status, ZX_OK);
173
174
if (Addr == WindowBase)
175
WindowBase += Size;
176
WindowSize -= Size;
177
}
178
}
179
180
bool MemMapFuchsia::remapImpl(uptr Addr, uptr Size, const char *Name,
181
uptr Flags) {
182
const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
183
const bool PreCommit = !!(Flags & MAP_PRECOMMIT);
184
const bool NoAccess = !!(Flags & MAP_NOACCESS);
185
186
// NOTE: This will rename the *whole* VMO, not only the requested portion of
187
// it. But we cannot do better than this given the MemMap API. In practice,
188
// the upper layers of Scudo always pass the same Name for a given MemMap.
189
if (Name != nullptr)
190
setVmoName(Vmo, Name);
191
192
uptr MappedAddr;
193
zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC_OVERWRITE;
194
if (!NoAccess)
195
MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
196
zx_status_t Status =
197
_zx_vmar_map(_zx_vmar_root_self(), MapFlags, Addr - getRootVmarBase(),
198
Vmo, Addr - MapAddr, Size, &MappedAddr);
199
if (UNLIKELY(Status != ZX_OK)) {
200
if (AllowNoMem && IsNoMemError(Status))
201
return false;
202
dieOnError(Status, "zx_vmar_map", Size);
203
}
204
DCHECK_EQ(Addr, MappedAddr);
205
206
if (PreCommit) {
207
Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,
208
Size, nullptr, 0);
209
CHECK_EQ(Status, ZX_OK);
210
}
211
212
return true;
213
}
214
215
void MemMapFuchsia::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) {
216
zx_status_t Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, From - MapAddr,
217
Size, nullptr, 0);
218
CHECK_EQ(Status, ZX_OK);
219
}
220
221
void MemMapFuchsia::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) {
222
const bool NoAccess = !!(Flags & MAP_NOACCESS);
223
224
zx_vm_option_t MapFlags = 0;
225
if (!NoAccess)
226
MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
227
zx_status_t Status =
228
_zx_vmar_protect(_zx_vmar_root_self(), MapFlags, Addr, Size);
229
CHECK_EQ(Status, ZX_OK);
230
}
231
232
bool ReservedMemoryFuchsia::createImpl(UNUSED uptr Addr, uptr Size,
233
UNUSED const char *Name, uptr Flags) {
234
const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
235
236
// Reserve memory by mapping the placeholder VMO without any permission.
237
zx_status_t Status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_ALLOW_FAULTS, 0,
238
getPlaceholderVmo(), 0, Size, &Base);
239
if (UNLIKELY(Status != ZX_OK)) {
240
if (AllowNoMem && IsNoMemError(Status))
241
return false;
242
dieOnError(Status, "zx_vmar_map", Size);
243
}
244
245
Capacity = Size;
246
return true;
247
}
248
249
void ReservedMemoryFuchsia::releaseImpl() {
250
zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(), Base, Capacity);
251
CHECK_EQ(Status, ZX_OK);
252
}
253
254
ReservedMemoryFuchsia::MemMapT ReservedMemoryFuchsia::dispatchImpl(uptr Addr,
255
uptr Size) {
256
return ReservedMemoryFuchsia::MemMapT(Addr, Size);
257
}
258
259
} // namespace scudo
260
261
#endif // SCUDO_FUCHSIA
262
263