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