106c3fb27SDimitry Andric //===-- mem_map_fuchsia.cpp -------------------------------------*- C++ -*-===// 206c3fb27SDimitry Andric // 306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606c3fb27SDimitry Andric // 706c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 806c3fb27SDimitry Andric 906c3fb27SDimitry Andric #include "mem_map_fuchsia.h" 1006c3fb27SDimitry Andric 1106c3fb27SDimitry Andric #include "atomic_helpers.h" 1206c3fb27SDimitry Andric #include "common.h" 1306c3fb27SDimitry Andric #include "string_utils.h" 1406c3fb27SDimitry Andric 1506c3fb27SDimitry Andric #if SCUDO_FUCHSIA 1606c3fb27SDimitry Andric 1706c3fb27SDimitry Andric #include <zircon/process.h> 1806c3fb27SDimitry Andric #include <zircon/status.h> 1906c3fb27SDimitry Andric #include <zircon/syscalls.h> 2006c3fb27SDimitry Andric 2106c3fb27SDimitry Andric namespace scudo { 2206c3fb27SDimitry Andric 2306c3fb27SDimitry Andric static void NORETURN dieOnError(zx_status_t Status, const char *FnName, 2406c3fb27SDimitry Andric uptr Size) { 25*0fca6ea1SDimitry Andric ScopedString Error; 26*0fca6ea1SDimitry Andric Error.append("SCUDO ERROR: %s failed with size %zuKB (%s)", FnName, 2706c3fb27SDimitry Andric Size >> 10, _zx_status_get_string(Status)); 28*0fca6ea1SDimitry Andric outputRaw(Error.data()); 2906c3fb27SDimitry Andric die(); 3006c3fb27SDimitry Andric } 3106c3fb27SDimitry Andric 3206c3fb27SDimitry Andric static void setVmoName(zx_handle_t Vmo, const char *Name) { 3306c3fb27SDimitry Andric size_t Len = strlen(Name); 3406c3fb27SDimitry Andric DCHECK_LT(Len, ZX_MAX_NAME_LEN); 3506c3fb27SDimitry Andric zx_status_t Status = _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, Len); 3606c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 3706c3fb27SDimitry Andric } 3806c3fb27SDimitry Andric 3906c3fb27SDimitry Andric // Returns the (cached) base address of the root VMAR. 4006c3fb27SDimitry Andric static uptr getRootVmarBase() { 4106c3fb27SDimitry Andric static atomic_uptr CachedResult = {0}; 4206c3fb27SDimitry Andric 435f757f3fSDimitry Andric uptr Result = atomic_load(&CachedResult, memory_order_acquire); 4406c3fb27SDimitry Andric if (UNLIKELY(!Result)) { 4506c3fb27SDimitry Andric zx_info_vmar_t VmarInfo; 4606c3fb27SDimitry Andric zx_status_t Status = 4706c3fb27SDimitry Andric _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &VmarInfo, 4806c3fb27SDimitry Andric sizeof(VmarInfo), nullptr, nullptr); 4906c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 5006c3fb27SDimitry Andric CHECK_NE(VmarInfo.base, 0); 5106c3fb27SDimitry Andric 525f757f3fSDimitry Andric atomic_store(&CachedResult, VmarInfo.base, memory_order_release); 5306c3fb27SDimitry Andric Result = VmarInfo.base; 5406c3fb27SDimitry Andric } 5506c3fb27SDimitry Andric 5606c3fb27SDimitry Andric return Result; 5706c3fb27SDimitry Andric } 5806c3fb27SDimitry Andric 5906c3fb27SDimitry Andric // Lazily creates and then always returns the same zero-sized VMO. 6006c3fb27SDimitry Andric static zx_handle_t getPlaceholderVmo() { 6106c3fb27SDimitry Andric static atomic_u32 StoredVmo = {ZX_HANDLE_INVALID}; 6206c3fb27SDimitry Andric 635f757f3fSDimitry Andric zx_handle_t Vmo = atomic_load(&StoredVmo, memory_order_acquire); 6406c3fb27SDimitry Andric if (UNLIKELY(Vmo == ZX_HANDLE_INVALID)) { 6506c3fb27SDimitry Andric // Create a zero-sized placeholder VMO. 6606c3fb27SDimitry Andric zx_status_t Status = _zx_vmo_create(0, 0, &Vmo); 6706c3fb27SDimitry Andric if (UNLIKELY(Status != ZX_OK)) 6806c3fb27SDimitry Andric dieOnError(Status, "zx_vmo_create", 0); 6906c3fb27SDimitry Andric 7006c3fb27SDimitry Andric setVmoName(Vmo, "scudo:reserved"); 7106c3fb27SDimitry Andric 7206c3fb27SDimitry Andric // Atomically store its handle. If some other thread wins the race, use its 7306c3fb27SDimitry Andric // handle and discard ours. 745f757f3fSDimitry Andric zx_handle_t OldValue = atomic_compare_exchange_strong( 755f757f3fSDimitry Andric &StoredVmo, ZX_HANDLE_INVALID, Vmo, memory_order_acq_rel); 765f757f3fSDimitry Andric if (UNLIKELY(OldValue != ZX_HANDLE_INVALID)) { 7706c3fb27SDimitry Andric Status = _zx_handle_close(Vmo); 7806c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 7906c3fb27SDimitry Andric 8006c3fb27SDimitry Andric Vmo = OldValue; 8106c3fb27SDimitry Andric } 8206c3fb27SDimitry Andric } 8306c3fb27SDimitry Andric 8406c3fb27SDimitry Andric return Vmo; 8506c3fb27SDimitry Andric } 8606c3fb27SDimitry Andric 87*0fca6ea1SDimitry Andric // Checks if MAP_ALLOWNOMEM allows the given error code. 88*0fca6ea1SDimitry Andric static bool IsNoMemError(zx_status_t Status) { 89*0fca6ea1SDimitry Andric // Note: _zx_vmar_map returns ZX_ERR_NO_RESOURCES if the VMAR does not contain 90*0fca6ea1SDimitry Andric // a suitable free spot. 91*0fca6ea1SDimitry Andric return Status == ZX_ERR_NO_MEMORY || Status == ZX_ERR_NO_RESOURCES; 92*0fca6ea1SDimitry Andric } 93*0fca6ea1SDimitry Andric 94*0fca6ea1SDimitry Andric // Note: this constructor is only called by ReservedMemoryFuchsia::dispatch. 9506c3fb27SDimitry Andric MemMapFuchsia::MemMapFuchsia(uptr Base, uptr Capacity) 9606c3fb27SDimitry Andric : MapAddr(Base), WindowBase(Base), WindowSize(Capacity) { 9706c3fb27SDimitry Andric // Create the VMO. 9806c3fb27SDimitry Andric zx_status_t Status = _zx_vmo_create(Capacity, 0, &Vmo); 9906c3fb27SDimitry Andric if (UNLIKELY(Status != ZX_OK)) 10006c3fb27SDimitry Andric dieOnError(Status, "zx_vmo_create", Capacity); 101*0fca6ea1SDimitry Andric 102*0fca6ea1SDimitry Andric setVmoName(Vmo, "scudo:dispatched"); 10306c3fb27SDimitry Andric } 10406c3fb27SDimitry Andric 10506c3fb27SDimitry Andric bool MemMapFuchsia::mapImpl(UNUSED uptr Addr, uptr Size, const char *Name, 10606c3fb27SDimitry Andric uptr Flags) { 10706c3fb27SDimitry Andric const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM); 10806c3fb27SDimitry Andric const bool PreCommit = !!(Flags & MAP_PRECOMMIT); 10906c3fb27SDimitry Andric const bool NoAccess = !!(Flags & MAP_NOACCESS); 11006c3fb27SDimitry Andric 11106c3fb27SDimitry Andric // Create the VMO. 11206c3fb27SDimitry Andric zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo); 11306c3fb27SDimitry Andric if (UNLIKELY(Status != ZX_OK)) { 114*0fca6ea1SDimitry Andric if (AllowNoMem && IsNoMemError(Status)) 11506c3fb27SDimitry Andric return false; 116*0fca6ea1SDimitry Andric dieOnError(Status, "zx_vmo_create", Size); 11706c3fb27SDimitry Andric } 11806c3fb27SDimitry Andric 11906c3fb27SDimitry Andric if (Name != nullptr) 12006c3fb27SDimitry Andric setVmoName(Vmo, Name); 12106c3fb27SDimitry Andric 12206c3fb27SDimitry Andric // Map it. 12306c3fb27SDimitry Andric zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS; 12406c3fb27SDimitry Andric if (!NoAccess) 12506c3fb27SDimitry Andric MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; 12606c3fb27SDimitry Andric Status = 12706c3fb27SDimitry Andric _zx_vmar_map(_zx_vmar_root_self(), MapFlags, 0, Vmo, 0, Size, &MapAddr); 12806c3fb27SDimitry Andric if (UNLIKELY(Status != ZX_OK)) { 129*0fca6ea1SDimitry Andric if (AllowNoMem && IsNoMemError(Status)) { 13006c3fb27SDimitry Andric Status = _zx_handle_close(Vmo); 13106c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 13206c3fb27SDimitry Andric 13306c3fb27SDimitry Andric MapAddr = 0; 13406c3fb27SDimitry Andric Vmo = ZX_HANDLE_INVALID; 13506c3fb27SDimitry Andric return false; 13606c3fb27SDimitry Andric } 137*0fca6ea1SDimitry Andric dieOnError(Status, "zx_vmar_map", Size); 138*0fca6ea1SDimitry Andric } 13906c3fb27SDimitry Andric 14006c3fb27SDimitry Andric if (PreCommit) { 14106c3fb27SDimitry Andric Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr, 14206c3fb27SDimitry Andric Size, nullptr, 0); 14306c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 14406c3fb27SDimitry Andric } 14506c3fb27SDimitry Andric 14606c3fb27SDimitry Andric WindowBase = MapAddr; 14706c3fb27SDimitry Andric WindowSize = Size; 14806c3fb27SDimitry Andric return true; 14906c3fb27SDimitry Andric } 15006c3fb27SDimitry Andric 15106c3fb27SDimitry Andric void MemMapFuchsia::unmapImpl(uptr Addr, uptr Size) { 15206c3fb27SDimitry Andric zx_status_t Status; 15306c3fb27SDimitry Andric 15406c3fb27SDimitry Andric if (Size == WindowSize) { 15506c3fb27SDimitry Andric // NOTE: Closing first and then unmapping seems slightly faster than doing 15606c3fb27SDimitry Andric // the same operations in the opposite order. 15706c3fb27SDimitry Andric Status = _zx_handle_close(Vmo); 15806c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 15906c3fb27SDimitry Andric Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size); 16006c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 16106c3fb27SDimitry Andric 16206c3fb27SDimitry Andric MapAddr = WindowBase = WindowSize = 0; 16306c3fb27SDimitry Andric Vmo = ZX_HANDLE_INVALID; 16406c3fb27SDimitry Andric } else { 16506c3fb27SDimitry Andric // Unmap the subrange. 16606c3fb27SDimitry Andric Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size); 16706c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 16806c3fb27SDimitry Andric 16906c3fb27SDimitry Andric // Decommit the pages that we just unmapped. 17006c3fb27SDimitry Andric Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, Addr - MapAddr, Size, 17106c3fb27SDimitry Andric nullptr, 0); 17206c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 17306c3fb27SDimitry Andric 17406c3fb27SDimitry Andric if (Addr == WindowBase) 17506c3fb27SDimitry Andric WindowBase += Size; 17606c3fb27SDimitry Andric WindowSize -= Size; 17706c3fb27SDimitry Andric } 17806c3fb27SDimitry Andric } 17906c3fb27SDimitry Andric 18006c3fb27SDimitry Andric bool MemMapFuchsia::remapImpl(uptr Addr, uptr Size, const char *Name, 18106c3fb27SDimitry Andric uptr Flags) { 18206c3fb27SDimitry Andric const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM); 18306c3fb27SDimitry Andric const bool PreCommit = !!(Flags & MAP_PRECOMMIT); 18406c3fb27SDimitry Andric const bool NoAccess = !!(Flags & MAP_NOACCESS); 18506c3fb27SDimitry Andric 18606c3fb27SDimitry Andric // NOTE: This will rename the *whole* VMO, not only the requested portion of 18706c3fb27SDimitry Andric // it. But we cannot do better than this given the MemMap API. In practice, 18806c3fb27SDimitry Andric // the upper layers of Scudo always pass the same Name for a given MemMap. 18906c3fb27SDimitry Andric if (Name != nullptr) 19006c3fb27SDimitry Andric setVmoName(Vmo, Name); 19106c3fb27SDimitry Andric 19206c3fb27SDimitry Andric uptr MappedAddr; 19306c3fb27SDimitry Andric zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC_OVERWRITE; 19406c3fb27SDimitry Andric if (!NoAccess) 19506c3fb27SDimitry Andric MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; 19606c3fb27SDimitry Andric zx_status_t Status = 19706c3fb27SDimitry Andric _zx_vmar_map(_zx_vmar_root_self(), MapFlags, Addr - getRootVmarBase(), 19806c3fb27SDimitry Andric Vmo, Addr - MapAddr, Size, &MappedAddr); 19906c3fb27SDimitry Andric if (UNLIKELY(Status != ZX_OK)) { 200*0fca6ea1SDimitry Andric if (AllowNoMem && IsNoMemError(Status)) 20106c3fb27SDimitry Andric return false; 202*0fca6ea1SDimitry Andric dieOnError(Status, "zx_vmar_map", Size); 20306c3fb27SDimitry Andric } 20406c3fb27SDimitry Andric DCHECK_EQ(Addr, MappedAddr); 20506c3fb27SDimitry Andric 20606c3fb27SDimitry Andric if (PreCommit) { 20706c3fb27SDimitry Andric Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr, 20806c3fb27SDimitry Andric Size, nullptr, 0); 20906c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 21006c3fb27SDimitry Andric } 21106c3fb27SDimitry Andric 21206c3fb27SDimitry Andric return true; 21306c3fb27SDimitry Andric } 21406c3fb27SDimitry Andric 21506c3fb27SDimitry Andric void MemMapFuchsia::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) { 21606c3fb27SDimitry Andric zx_status_t Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, From - MapAddr, 21706c3fb27SDimitry Andric Size, nullptr, 0); 21806c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 21906c3fb27SDimitry Andric } 22006c3fb27SDimitry Andric 22106c3fb27SDimitry Andric void MemMapFuchsia::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) { 22206c3fb27SDimitry Andric const bool NoAccess = !!(Flags & MAP_NOACCESS); 22306c3fb27SDimitry Andric 22406c3fb27SDimitry Andric zx_vm_option_t MapFlags = 0; 22506c3fb27SDimitry Andric if (!NoAccess) 22606c3fb27SDimitry Andric MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; 22706c3fb27SDimitry Andric zx_status_t Status = 22806c3fb27SDimitry Andric _zx_vmar_protect(_zx_vmar_root_self(), MapFlags, Addr, Size); 22906c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 23006c3fb27SDimitry Andric } 23106c3fb27SDimitry Andric 23206c3fb27SDimitry Andric bool ReservedMemoryFuchsia::createImpl(UNUSED uptr Addr, uptr Size, 23306c3fb27SDimitry Andric UNUSED const char *Name, uptr Flags) { 23406c3fb27SDimitry Andric const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM); 23506c3fb27SDimitry Andric 23606c3fb27SDimitry Andric // Reserve memory by mapping the placeholder VMO without any permission. 23706c3fb27SDimitry Andric zx_status_t Status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_ALLOW_FAULTS, 0, 23806c3fb27SDimitry Andric getPlaceholderVmo(), 0, Size, &Base); 23906c3fb27SDimitry Andric if (UNLIKELY(Status != ZX_OK)) { 240*0fca6ea1SDimitry Andric if (AllowNoMem && IsNoMemError(Status)) 24106c3fb27SDimitry Andric return false; 242*0fca6ea1SDimitry Andric dieOnError(Status, "zx_vmar_map", Size); 24306c3fb27SDimitry Andric } 24406c3fb27SDimitry Andric 24506c3fb27SDimitry Andric Capacity = Size; 24606c3fb27SDimitry Andric return true; 24706c3fb27SDimitry Andric } 24806c3fb27SDimitry Andric 24906c3fb27SDimitry Andric void ReservedMemoryFuchsia::releaseImpl() { 25006c3fb27SDimitry Andric zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(), Base, Capacity); 25106c3fb27SDimitry Andric CHECK_EQ(Status, ZX_OK); 25206c3fb27SDimitry Andric } 25306c3fb27SDimitry Andric 25406c3fb27SDimitry Andric ReservedMemoryFuchsia::MemMapT ReservedMemoryFuchsia::dispatchImpl(uptr Addr, 25506c3fb27SDimitry Andric uptr Size) { 25606c3fb27SDimitry Andric return ReservedMemoryFuchsia::MemMapT(Addr, Size); 25706c3fb27SDimitry Andric } 25806c3fb27SDimitry Andric 25906c3fb27SDimitry Andric } // namespace scudo 26006c3fb27SDimitry Andric 26106c3fb27SDimitry Andric #endif // SCUDO_FUCHSIA 262