1 //===-- mem_map_fuchsia.cpp -------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "mem_map_fuchsia.h"
11 #include "atomic_helpers.h"
13 #include "string_utils.h"
17 #include <zircon/process.h>
18 #include <zircon/status.h>
19 #include <zircon/syscalls.h>
23 static void NORETURN
dieOnError(zx_status_t Status
, const char *FnName
,
26 formatString(Error
, sizeof(Error
),
27 "SCUDO ERROR: %s failed with size %zuKB (%s)", FnName
,
28 Size
>> 10, _zx_status_get_string(Status
));
33 static void setVmoName(zx_handle_t Vmo
, const char *Name
) {
34 size_t Len
= strlen(Name
);
35 DCHECK_LT(Len
, ZX_MAX_NAME_LEN
);
36 zx_status_t Status
= _zx_object_set_property(Vmo
, ZX_PROP_NAME
, Name
, Len
);
37 CHECK_EQ(Status
, ZX_OK
);
40 // Returns the (cached) base address of the root VMAR.
41 static uptr
getRootVmarBase() {
42 static atomic_uptr CachedResult
= {0};
44 uptr Result
= atomic_load(&CachedResult
, memory_order_acquire
);
45 if (UNLIKELY(!Result
)) {
46 zx_info_vmar_t VmarInfo
;
48 _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR
, &VmarInfo
,
49 sizeof(VmarInfo
), nullptr, nullptr);
50 CHECK_EQ(Status
, ZX_OK
);
51 CHECK_NE(VmarInfo
.base
, 0);
53 atomic_store(&CachedResult
, VmarInfo
.base
, memory_order_release
);
54 Result
= VmarInfo
.base
;
60 // Lazily creates and then always returns the same zero-sized VMO.
61 static zx_handle_t
getPlaceholderVmo() {
62 static atomic_u32 StoredVmo
= {ZX_HANDLE_INVALID
};
64 zx_handle_t Vmo
= atomic_load(&StoredVmo
, memory_order_acquire
);
65 if (UNLIKELY(Vmo
== ZX_HANDLE_INVALID
)) {
66 // Create a zero-sized placeholder VMO.
67 zx_status_t Status
= _zx_vmo_create(0, 0, &Vmo
);
68 if (UNLIKELY(Status
!= ZX_OK
))
69 dieOnError(Status
, "zx_vmo_create", 0);
71 setVmoName(Vmo
, "scudo:reserved");
73 // Atomically store its handle. If some other thread wins the race, use its
74 // handle and discard ours.
75 zx_handle_t OldValue
= atomic_compare_exchange_strong(
76 &StoredVmo
, ZX_HANDLE_INVALID
, Vmo
, memory_order_acq_rel
);
77 if (UNLIKELY(OldValue
!= ZX_HANDLE_INVALID
)) {
78 Status
= _zx_handle_close(Vmo
);
79 CHECK_EQ(Status
, ZX_OK
);
88 MemMapFuchsia::MemMapFuchsia(uptr Base
, uptr Capacity
)
89 : MapAddr(Base
), WindowBase(Base
), WindowSize(Capacity
) {
91 zx_status_t Status
= _zx_vmo_create(Capacity
, 0, &Vmo
);
92 if (UNLIKELY(Status
!= ZX_OK
))
93 dieOnError(Status
, "zx_vmo_create", Capacity
);
96 bool MemMapFuchsia::mapImpl(UNUSED uptr Addr
, uptr Size
, const char *Name
,
98 const bool AllowNoMem
= !!(Flags
& MAP_ALLOWNOMEM
);
99 const bool PreCommit
= !!(Flags
& MAP_PRECOMMIT
);
100 const bool NoAccess
= !!(Flags
& MAP_NOACCESS
);
103 zx_status_t Status
= _zx_vmo_create(Size
, 0, &Vmo
);
104 if (UNLIKELY(Status
!= ZX_OK
)) {
105 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
106 dieOnError(Status
, "zx_vmo_create", Size
);
111 setVmoName(Vmo
, Name
);
114 zx_vm_option_t MapFlags
= ZX_VM_ALLOW_FAULTS
;
116 MapFlags
|= ZX_VM_PERM_READ
| ZX_VM_PERM_WRITE
;
118 _zx_vmar_map(_zx_vmar_root_self(), MapFlags
, 0, Vmo
, 0, Size
, &MapAddr
);
119 if (UNLIKELY(Status
!= ZX_OK
)) {
120 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
121 dieOnError(Status
, "zx_vmar_map", Size
);
123 Status
= _zx_handle_close(Vmo
);
124 CHECK_EQ(Status
, ZX_OK
);
127 Vmo
= ZX_HANDLE_INVALID
;
132 Status
= _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT
, MapAddr
,
134 CHECK_EQ(Status
, ZX_OK
);
137 WindowBase
= MapAddr
;
142 void MemMapFuchsia::unmapImpl(uptr Addr
, uptr Size
) {
145 if (Size
== WindowSize
) {
146 // NOTE: Closing first and then unmapping seems slightly faster than doing
147 // the same operations in the opposite order.
148 Status
= _zx_handle_close(Vmo
);
149 CHECK_EQ(Status
, ZX_OK
);
150 Status
= _zx_vmar_unmap(_zx_vmar_root_self(), Addr
, Size
);
151 CHECK_EQ(Status
, ZX_OK
);
153 MapAddr
= WindowBase
= WindowSize
= 0;
154 Vmo
= ZX_HANDLE_INVALID
;
156 // Unmap the subrange.
157 Status
= _zx_vmar_unmap(_zx_vmar_root_self(), Addr
, Size
);
158 CHECK_EQ(Status
, ZX_OK
);
160 // Decommit the pages that we just unmapped.
161 Status
= _zx_vmo_op_range(Vmo
, ZX_VMO_OP_DECOMMIT
, Addr
- MapAddr
, Size
,
163 CHECK_EQ(Status
, ZX_OK
);
165 if (Addr
== WindowBase
)
171 bool MemMapFuchsia::remapImpl(uptr Addr
, uptr Size
, const char *Name
,
173 const bool AllowNoMem
= !!(Flags
& MAP_ALLOWNOMEM
);
174 const bool PreCommit
= !!(Flags
& MAP_PRECOMMIT
);
175 const bool NoAccess
= !!(Flags
& MAP_NOACCESS
);
177 // NOTE: This will rename the *whole* VMO, not only the requested portion of
178 // it. But we cannot do better than this given the MemMap API. In practice,
179 // the upper layers of Scudo always pass the same Name for a given MemMap.
181 setVmoName(Vmo
, Name
);
184 zx_vm_option_t MapFlags
= ZX_VM_ALLOW_FAULTS
| ZX_VM_SPECIFIC_OVERWRITE
;
186 MapFlags
|= ZX_VM_PERM_READ
| ZX_VM_PERM_WRITE
;
188 _zx_vmar_map(_zx_vmar_root_self(), MapFlags
, Addr
- getRootVmarBase(),
189 Vmo
, Addr
- MapAddr
, Size
, &MappedAddr
);
190 if (UNLIKELY(Status
!= ZX_OK
)) {
191 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
192 dieOnError(Status
, "zx_vmar_map", Size
);
195 DCHECK_EQ(Addr
, MappedAddr
);
198 Status
= _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT
, MapAddr
,
200 CHECK_EQ(Status
, ZX_OK
);
206 void MemMapFuchsia::releaseAndZeroPagesToOSImpl(uptr From
, uptr Size
) {
207 zx_status_t Status
= _zx_vmo_op_range(Vmo
, ZX_VMO_OP_DECOMMIT
, From
- MapAddr
,
209 CHECK_EQ(Status
, ZX_OK
);
212 void MemMapFuchsia::setMemoryPermissionImpl(uptr Addr
, uptr Size
, uptr Flags
) {
213 const bool NoAccess
= !!(Flags
& MAP_NOACCESS
);
215 zx_vm_option_t MapFlags
= 0;
217 MapFlags
|= ZX_VM_PERM_READ
| ZX_VM_PERM_WRITE
;
219 _zx_vmar_protect(_zx_vmar_root_self(), MapFlags
, Addr
, Size
);
220 CHECK_EQ(Status
, ZX_OK
);
223 bool ReservedMemoryFuchsia::createImpl(UNUSED uptr Addr
, uptr Size
,
224 UNUSED
const char *Name
, uptr Flags
) {
225 const bool AllowNoMem
= !!(Flags
& MAP_ALLOWNOMEM
);
227 // Reserve memory by mapping the placeholder VMO without any permission.
228 zx_status_t Status
= _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_ALLOW_FAULTS
, 0,
229 getPlaceholderVmo(), 0, Size
, &Base
);
230 if (UNLIKELY(Status
!= ZX_OK
)) {
231 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
232 dieOnError(Status
, "zx_vmar_map", Size
);
240 void ReservedMemoryFuchsia::releaseImpl() {
241 zx_status_t Status
= _zx_vmar_unmap(_zx_vmar_root_self(), Base
, Capacity
);
242 CHECK_EQ(Status
, ZX_OK
);
245 ReservedMemoryFuchsia::MemMapT
ReservedMemoryFuchsia::dispatchImpl(uptr Addr
,
247 return ReservedMemoryFuchsia::MemMapT(Addr
, Size
);
252 #endif // SCUDO_FUCHSIA