1 //===-- 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 //===----------------------------------------------------------------------===//
15 #include "string_utils.h"
17 #include <lib/sync/mutex.h> // for sync_mutex_t
18 #include <stdlib.h> // for getenv()
19 #include <zircon/compiler.h>
20 #include <zircon/process.h>
21 #include <zircon/sanitizer.h>
22 #include <zircon/status.h>
23 #include <zircon/syscalls.h>
27 uptr
getPageSize() { return _zx_system_get_page_size(); }
29 void NORETURN
die() { __builtin_trap(); }
31 // We zero-initialize the Extra parameter of map(), make sure this is consistent
32 // with ZX_HANDLE_INVALID.
33 static_assert(ZX_HANDLE_INVALID
== 0, "");
35 static void NORETURN
dieOnError(zx_status_t Status
, const char *FnName
,
38 formatString(Error
, sizeof(Error
),
39 "SCUDO ERROR: %s failed with size %zuKB (%s)", FnName
,
40 Size
>> 10, zx_status_get_string(Status
));
45 static void *allocateVmar(uptr Size
, MapPlatformData
*Data
, bool AllowNoMem
) {
46 // Only scenario so far.
48 DCHECK_EQ(Data
->Vmar
, ZX_HANDLE_INVALID
);
50 const zx_status_t Status
= _zx_vmar_allocate(
52 ZX_VM_CAN_MAP_READ
| ZX_VM_CAN_MAP_WRITE
| ZX_VM_CAN_MAP_SPECIFIC
, 0,
53 Size
, &Data
->Vmar
, &Data
->VmarBase
);
54 if (UNLIKELY(Status
!= ZX_OK
)) {
55 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
56 dieOnError(Status
, "zx_vmar_allocate", Size
);
59 return reinterpret_cast<void *>(Data
->VmarBase
);
62 void *map(void *Addr
, uptr Size
, const char *Name
, uptr Flags
,
63 MapPlatformData
*Data
) {
64 DCHECK_EQ(Size
% getPageSizeCached(), 0);
65 const bool AllowNoMem
= !!(Flags
& MAP_ALLOWNOMEM
);
67 // For MAP_NOACCESS, just allocate a Vmar and return.
68 if (Flags
& MAP_NOACCESS
)
69 return allocateVmar(Size
, Data
, AllowNoMem
);
71 const zx_handle_t Vmar
= (Data
&& Data
->Vmar
!= ZX_HANDLE_INVALID
)
73 : _zx_vmar_root_self();
78 if (Data
&& Data
->Vmo
!= ZX_HANDLE_INVALID
) {
79 // If a Vmo was specified, it's a resize operation.
81 DCHECK(Flags
& MAP_RESIZABLE
);
83 VmoSize
= Data
->VmoSize
;
84 Status
= _zx_vmo_set_size(Vmo
, VmoSize
+ Size
);
85 if (Status
!= ZX_OK
) {
86 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
87 dieOnError(Status
, "zx_vmo_set_size", VmoSize
+ Size
);
91 // Otherwise, create a Vmo and set its name.
92 Status
= _zx_vmo_create(Size
, ZX_VMO_RESIZABLE
, &Vmo
);
93 if (UNLIKELY(Status
!= ZX_OK
)) {
94 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
95 dieOnError(Status
, "zx_vmo_create", Size
);
98 _zx_object_set_property(Vmo
, ZX_PROP_NAME
, Name
, strlen(Name
));
102 zx_vm_option_t MapFlags
=
103 ZX_VM_PERM_READ
| ZX_VM_PERM_WRITE
| ZX_VM_ALLOW_FAULTS
;
106 const uint64_t Offset
=
107 Addr
? reinterpret_cast<uintptr_t>(Addr
) - Data
->VmarBase
: 0;
109 MapFlags
|= ZX_VM_SPECIFIC
;
110 Status
= _zx_vmar_map(Vmar
, MapFlags
, Offset
, Vmo
, VmoSize
, Size
, &P
);
111 if (UNLIKELY(Status
!= ZX_OK
)) {
112 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
113 dieOnError(Status
, "zx_vmar_map", Size
);
117 if (Flags
& MAP_PRECOMMIT
) {
118 Status
= _zx_vmar_op_range(Vmar
, ZX_VMAR_OP_COMMIT
, P
, Size
,
119 /*buffer=*/nullptr, /*buffer_size=*/0);
122 // No need to track the Vmo if we don't intend on resizing it. Close it.
123 if (Flags
& MAP_RESIZABLE
) {
125 if (Data
->Vmo
== ZX_HANDLE_INVALID
)
128 DCHECK_EQ(Data
->Vmo
, Vmo
);
130 CHECK_EQ(_zx_handle_close(Vmo
), ZX_OK
);
132 if (UNLIKELY(Status
!= ZX_OK
)) {
133 if (Status
!= ZX_ERR_NO_MEMORY
|| !AllowNoMem
)
134 dieOnError(Status
, "zx_vmar_op_range", Size
);
139 Data
->VmoSize
+= Size
;
141 return reinterpret_cast<void *>(P
);
144 void unmap(void *Addr
, uptr Size
, uptr Flags
, MapPlatformData
*Data
) {
145 if (Flags
& UNMAP_ALL
) {
146 DCHECK_NE(Data
, nullptr);
147 const zx_handle_t Vmar
= Data
->Vmar
;
148 DCHECK_NE(Vmar
, _zx_vmar_root_self());
149 // Destroying the vmar effectively unmaps the whole mapping.
150 CHECK_EQ(_zx_vmar_destroy(Vmar
), ZX_OK
);
151 CHECK_EQ(_zx_handle_close(Vmar
), ZX_OK
);
153 const zx_handle_t Vmar
= (Data
&& Data
->Vmar
!= ZX_HANDLE_INVALID
)
155 : _zx_vmar_root_self();
156 const zx_status_t Status
=
157 _zx_vmar_unmap(Vmar
, reinterpret_cast<uintptr_t>(Addr
), Size
);
158 if (UNLIKELY(Status
!= ZX_OK
))
159 dieOnError(Status
, "zx_vmar_unmap", Size
);
162 if (Data
->Vmo
!= ZX_HANDLE_INVALID
)
163 CHECK_EQ(_zx_handle_close(Data
->Vmo
), ZX_OK
);
164 memset(Data
, 0, sizeof(*Data
));
168 void setMemoryPermission(UNUSED uptr Addr
, UNUSED uptr Size
, UNUSED uptr Flags
,
169 UNUSED MapPlatformData
*Data
) {
170 const zx_vm_option_t Prot
=
171 (Flags
& MAP_NOACCESS
) ? 0 : (ZX_VM_PERM_READ
| ZX_VM_PERM_WRITE
);
173 DCHECK_NE(Data
->Vmar
, ZX_HANDLE_INVALID
);
174 const zx_status_t Status
= _zx_vmar_protect(Data
->Vmar
, Prot
, Addr
, Size
);
176 dieOnError(Status
, "zx_vmar_protect", Size
);
179 void releasePagesToOS(UNUSED uptr BaseAddress
, uptr Offset
, uptr Size
,
180 MapPlatformData
*Data
) {
181 // TODO: DCHECK the BaseAddress is consistent with the data in
184 DCHECK_NE(Data
->Vmar
, ZX_HANDLE_INVALID
);
185 DCHECK_NE(Data
->Vmo
, ZX_HANDLE_INVALID
);
186 const zx_status_t Status
=
187 _zx_vmo_op_range(Data
->Vmo
, ZX_VMO_OP_DECOMMIT
, Offset
, Size
, NULL
, 0);
188 CHECK_EQ(Status
, ZX_OK
);
191 const char *getEnv(const char *Name
) { return getenv(Name
); }
193 // Note: we need to flag these methods with __TA_NO_THREAD_SAFETY_ANALYSIS
194 // because the Fuchsia implementation of sync_mutex_t has clang thread safety
195 // annotations. Were we to apply proper capability annotations to the top level
196 // HybridMutex class itself, they would not be needed. As it stands, the
197 // thread analysis thinks that we are locking the mutex and accidentally leaving
198 // it locked on the way out.
199 bool HybridMutex::tryLock() __TA_NO_THREAD_SAFETY_ANALYSIS
{
200 // Size and alignment must be compatible between both types.
201 return sync_mutex_trylock(&M
) == ZX_OK
;
204 void HybridMutex::lockSlow() __TA_NO_THREAD_SAFETY_ANALYSIS
{
208 void HybridMutex::unlock() __TA_NO_THREAD_SAFETY_ANALYSIS
{
209 sync_mutex_unlock(&M
);
212 void HybridMutex::assertHeldImpl() __TA_NO_THREAD_SAFETY_ANALYSIS
{}
214 u64
getMonotonicTime() { return _zx_clock_get_monotonic(); }
215 u64
getMonotonicTimeFast() { return _zx_clock_get_monotonic(); }
217 u32
getNumberOfCPUs() { return _zx_system_get_num_cpus(); }
219 u32
getThreadID() { return 0; }
221 bool getRandom(void *Buffer
, uptr Length
, UNUSED
bool Blocking
) {
222 static_assert(MaxRandomLength
<= ZX_CPRNG_DRAW_MAX_LEN
, "");
223 if (UNLIKELY(!Buffer
|| !Length
|| Length
> MaxRandomLength
))
225 _zx_cprng_draw(Buffer
, Length
);
229 void outputRaw(const char *Buffer
) {
230 __sanitizer_log_write(Buffer
, strlen(Buffer
));
233 void setAbortMessage(const char *Message
) {}
237 #endif // SCUDO_FUCHSIA