mem_map_fuchsia.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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. #include "mem_map_fuchsia.h"
  9. #include "atomic_helpers.h"
  10. #include "common.h"
  11. #include "string_utils.h"
  12. #if SCUDO_FUCHSIA
  13. #error #include <zircon/process.h>
  14. #error #include <zircon/status.h>
  15. #error #include <zircon/syscalls.h>
  16. namespace scudo {
  17. static void NORETURN dieOnError(zx_status_t Status, const char *FnName,
  18. uptr Size) {
  19. char Error[128];
  20. formatString(Error, sizeof(Error),
  21. "SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,
  22. Size >> 10, _zx_status_get_string(Status));
  23. outputRaw(Error);
  24. die();
  25. }
  26. static void setVmoName(zx_handle_t Vmo, const char *Name) {
  27. size_t Len = strlen(Name);
  28. DCHECK_LT(Len, ZX_MAX_NAME_LEN);
  29. zx_status_t Status = _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, Len);
  30. CHECK_EQ(Status, ZX_OK);
  31. }
  32. // Returns the (cached) base address of the root VMAR.
  33. static uptr getRootVmarBase() {
  34. static atomic_uptr CachedResult = {0};
  35. uptr Result = atomic_load(&CachedResult, memory_order_acquire);
  36. if (UNLIKELY(!Result)) {
  37. zx_info_vmar_t VmarInfo;
  38. zx_status_t Status =
  39. _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &VmarInfo,
  40. sizeof(VmarInfo), nullptr, nullptr);
  41. CHECK_EQ(Status, ZX_OK);
  42. CHECK_NE(VmarInfo.base, 0);
  43. atomic_store(&CachedResult, VmarInfo.base, memory_order_release);
  44. Result = VmarInfo.base;
  45. }
  46. return Result;
  47. }
  48. // Lazily creates and then always returns the same zero-sized VMO.
  49. static zx_handle_t getPlaceholderVmo() {
  50. static atomic_u32 StoredVmo = {ZX_HANDLE_INVALID};
  51. zx_handle_t Vmo = atomic_load(&StoredVmo, memory_order_acquire);
  52. if (UNLIKELY(Vmo == ZX_HANDLE_INVALID)) {
  53. // Create a zero-sized placeholder VMO.
  54. zx_status_t Status = _zx_vmo_create(0, 0, &Vmo);
  55. if (UNLIKELY(Status != ZX_OK))
  56. dieOnError(Status, "zx_vmo_create", 0);
  57. setVmoName(Vmo, "scudo:reserved");
  58. // Atomically store its handle. If some other thread wins the race, use its
  59. // handle and discard ours.
  60. zx_handle_t OldValue = atomic_compare_exchange_strong(
  61. &StoredVmo, ZX_HANDLE_INVALID, Vmo, memory_order_acq_rel);
  62. if (UNLIKELY(OldValue != ZX_HANDLE_INVALID)) {
  63. Status = _zx_handle_close(Vmo);
  64. CHECK_EQ(Status, ZX_OK);
  65. Vmo = OldValue;
  66. }
  67. }
  68. return Vmo;
  69. }
  70. MemMapFuchsia::MemMapFuchsia(uptr Base, uptr Capacity)
  71. : MapAddr(Base), WindowBase(Base), WindowSize(Capacity) {
  72. // Create the VMO.
  73. zx_status_t Status = _zx_vmo_create(Capacity, 0, &Vmo);
  74. if (UNLIKELY(Status != ZX_OK))
  75. dieOnError(Status, "zx_vmo_create", Capacity);
  76. }
  77. bool MemMapFuchsia::mapImpl(UNUSED uptr Addr, uptr Size, const char *Name,
  78. uptr Flags) {
  79. const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
  80. const bool PreCommit = !!(Flags & MAP_PRECOMMIT);
  81. const bool NoAccess = !!(Flags & MAP_NOACCESS);
  82. // Create the VMO.
  83. zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);
  84. if (UNLIKELY(Status != ZX_OK)) {
  85. if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
  86. dieOnError(Status, "zx_vmo_create", Size);
  87. return false;
  88. }
  89. if (Name != nullptr)
  90. setVmoName(Vmo, Name);
  91. // Map it.
  92. zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS;
  93. if (!NoAccess)
  94. MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
  95. Status =
  96. _zx_vmar_map(_zx_vmar_root_self(), MapFlags, 0, Vmo, 0, Size, &MapAddr);
  97. if (UNLIKELY(Status != ZX_OK)) {
  98. if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
  99. dieOnError(Status, "zx_vmar_map", Size);
  100. Status = _zx_handle_close(Vmo);
  101. CHECK_EQ(Status, ZX_OK);
  102. MapAddr = 0;
  103. Vmo = ZX_HANDLE_INVALID;
  104. return false;
  105. }
  106. if (PreCommit) {
  107. Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,
  108. Size, nullptr, 0);
  109. CHECK_EQ(Status, ZX_OK);
  110. }
  111. WindowBase = MapAddr;
  112. WindowSize = Size;
  113. return true;
  114. }
  115. void MemMapFuchsia::unmapImpl(uptr Addr, uptr Size) {
  116. zx_status_t Status;
  117. if (Size == WindowSize) {
  118. // NOTE: Closing first and then unmapping seems slightly faster than doing
  119. // the same operations in the opposite order.
  120. Status = _zx_handle_close(Vmo);
  121. CHECK_EQ(Status, ZX_OK);
  122. Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);
  123. CHECK_EQ(Status, ZX_OK);
  124. MapAddr = WindowBase = WindowSize = 0;
  125. Vmo = ZX_HANDLE_INVALID;
  126. } else {
  127. // Unmap the subrange.
  128. Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);
  129. CHECK_EQ(Status, ZX_OK);
  130. // Decommit the pages that we just unmapped.
  131. Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, Addr - MapAddr, Size,
  132. nullptr, 0);
  133. CHECK_EQ(Status, ZX_OK);
  134. if (Addr == WindowBase)
  135. WindowBase += Size;
  136. WindowSize -= Size;
  137. }
  138. }
  139. bool MemMapFuchsia::remapImpl(uptr Addr, uptr Size, const char *Name,
  140. uptr Flags) {
  141. const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
  142. const bool PreCommit = !!(Flags & MAP_PRECOMMIT);
  143. const bool NoAccess = !!(Flags & MAP_NOACCESS);
  144. // NOTE: This will rename the *whole* VMO, not only the requested portion of
  145. // it. But we cannot do better than this given the MemMap API. In practice,
  146. // the upper layers of Scudo always pass the same Name for a given MemMap.
  147. if (Name != nullptr)
  148. setVmoName(Vmo, Name);
  149. uptr MappedAddr;
  150. zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC_OVERWRITE;
  151. if (!NoAccess)
  152. MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
  153. zx_status_t Status =
  154. _zx_vmar_map(_zx_vmar_root_self(), MapFlags, Addr - getRootVmarBase(),
  155. Vmo, Addr - MapAddr, Size, &MappedAddr);
  156. if (UNLIKELY(Status != ZX_OK)) {
  157. if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
  158. dieOnError(Status, "zx_vmar_map", Size);
  159. return false;
  160. }
  161. DCHECK_EQ(Addr, MappedAddr);
  162. if (PreCommit) {
  163. Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,
  164. Size, nullptr, 0);
  165. CHECK_EQ(Status, ZX_OK);
  166. }
  167. return true;
  168. }
  169. void MemMapFuchsia::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) {
  170. zx_status_t Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, From - MapAddr,
  171. Size, nullptr, 0);
  172. CHECK_EQ(Status, ZX_OK);
  173. }
  174. void MemMapFuchsia::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) {
  175. const bool NoAccess = !!(Flags & MAP_NOACCESS);
  176. zx_vm_option_t MapFlags = 0;
  177. if (!NoAccess)
  178. MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
  179. zx_status_t Status =
  180. _zx_vmar_protect(_zx_vmar_root_self(), MapFlags, Addr, Size);
  181. CHECK_EQ(Status, ZX_OK);
  182. }
  183. bool ReservedMemoryFuchsia::createImpl(UNUSED uptr Addr, uptr Size,
  184. UNUSED const char *Name, uptr Flags) {
  185. const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);
  186. // Reserve memory by mapping the placeholder VMO without any permission.
  187. zx_status_t Status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_ALLOW_FAULTS, 0,
  188. getPlaceholderVmo(), 0, Size, &Base);
  189. if (UNLIKELY(Status != ZX_OK)) {
  190. if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
  191. dieOnError(Status, "zx_vmar_map", Size);
  192. return false;
  193. }
  194. Capacity = Size;
  195. return true;
  196. }
  197. void ReservedMemoryFuchsia::releaseImpl() {
  198. zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(), Base, Capacity);
  199. CHECK_EQ(Status, ZX_OK);
  200. }
  201. ReservedMemoryFuchsia::MemMapT ReservedMemoryFuchsia::dispatchImpl(uptr Addr,
  202. uptr Size) {
  203. return ReservedMemoryFuchsia::MemMapT(Addr, Size);
  204. }
  205. } // namespace scudo
  206. #endif // SCUDO_FUCHSIA