1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "os/mem.h"
17 #include "utils/type_helpers.h"
18 #include "utils/asan_interface.h"
19 #include "utils/tsan_interface.h"
20
21 #include <limits>
22 #include <sys/mman.h>
23 #include <unistd.h>
24
25 #include <type_traits>
26
27 #if defined(__GLIBC__) || defined(PANDA_TARGET_MOBILE)
28 #include <malloc.h>
29 #endif
30
31 namespace panda::os::mem {
32
MmapDeleter(std::byte * ptr,size_t size)33 void MmapDeleter(std::byte *ptr, size_t size) noexcept
34 {
35 if (ptr != nullptr) {
36 munmap(ptr, size);
37 }
38 }
39
MapFile(file::File file,uint32_t prot,uint32_t flags,size_t size,size_t file_offset,void * hint)40 BytePtr MapFile(file::File file, uint32_t prot, uint32_t flags, size_t size, size_t file_offset, void *hint)
41 {
42 size_t map_offset = RoundDown(file_offset, GetPageSize());
43 size_t offset = file_offset - map_offset;
44 size_t map_size = size + offset;
45 void *result = mmap(hint, map_size, prot, flags, file.GetFd(), map_offset);
46 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
47 if (result == MAP_FAILED) {
48 return BytePtr(nullptr, 0, MmapDeleter);
49 }
50
51 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
52 return BytePtr(static_cast<std::byte *>(result) + offset, size, offset, MmapDeleter);
53 }
54
MapExecuted(size_t size)55 BytePtr MapExecuted(size_t size)
56 {
57 // By design caller should pass valid size, so don't do any additional checks except ones that
58 // mmap do itself
59 // NOLINTNEXTLINE(hicpp-signed-bitwise)
60 void *result = mmap(nullptr, size, PROT_EXEC | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
61 if (result == reinterpret_cast<void *>(-1)) {
62 result = nullptr;
63 }
64
65 return BytePtr(static_cast<std::byte *>(result), (result == nullptr) ? 0 : size, MmapDeleter);
66 }
67
MakeMemWithProtFlag(void * mem,size_t size,int prot)68 std::optional<Error> MakeMemWithProtFlag(void *mem, size_t size, int prot)
69 {
70 int r = mprotect(mem, size, prot);
71 if (r != 0) {
72 return Error(errno);
73 }
74 return {};
75 }
76
MakeMemReadExec(void * mem,size_t size)77 std::optional<Error> MakeMemReadExec(void *mem, size_t size)
78 {
79 // NOLINTNEXTLINE(hicpp-signed-bitwise)
80 return MakeMemWithProtFlag(mem, size, PROT_EXEC | PROT_READ);
81 }
82
MakeMemReadWrite(void * mem,size_t size)83 std::optional<Error> MakeMemReadWrite(void *mem, size_t size)
84 {
85 // NOLINTNEXTLINE(hicpp-signed-bitwise)
86 return MakeMemWithProtFlag(mem, size, PROT_WRITE | PROT_READ);
87 }
88
MakeMemReadOnly(void * mem,size_t size)89 std::optional<Error> MakeMemReadOnly(void *mem, size_t size)
90 {
91 return MakeMemWithProtFlag(mem, size, PROT_READ);
92 }
93
MakeMemProtected(void * mem,size_t size)94 std::optional<Error> MakeMemProtected(void *mem, size_t size)
95 {
96 return MakeMemWithProtFlag(mem, size, PROT_NONE);
97 }
98
AlignDownToPageSize(uintptr_t addr)99 uintptr_t AlignDownToPageSize(uintptr_t addr)
100 {
101 const auto SYS_PAGE_SIZE = static_cast<size_t>(sysconf(_SC_PAGESIZE));
102 addr &= ~(SYS_PAGE_SIZE - 1);
103 return addr;
104 }
105
AlignedAlloc(size_t alignment_in_bytes,size_t size)106 void *AlignedAlloc(size_t alignment_in_bytes, size_t size)
107 {
108 size_t aligned_size = (size + alignment_in_bytes - 1) & ~(alignment_in_bytes - 1);
109 #if defined PANDA_TARGET_MOBILE || defined PANDA_TARGET_MACOS || defined PANDA_TARGET_IOS || defined PANDA_TARGET_ARKUI_X
110 void *ret = nullptr;
111 int r = posix_memalign(reinterpret_cast<void **>(&ret), alignment_in_bytes, aligned_size);
112 if (r != 0) {
113 std::cerr << "posix_memalign failed, code: " << r << std::endl;
114 ASSERT(0);
115 }
116 #else
117 auto ret = aligned_alloc(alignment_in_bytes, aligned_size);
118 #endif
119 ASSERT(reinterpret_cast<uintptr_t>(ret) == (reinterpret_cast<uintptr_t>(ret) & ~(alignment_in_bytes - 1)));
120 return ret;
121 }
122
AlignedFree(void * mem)123 void AlignedFree(void *mem)
124 {
125 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
126 std::free(mem);
127 }
128
GetPageSizeFromOs()129 static uint32_t GetPageSizeFromOs()
130 {
131 // NOLINTNEXTLINE(google-runtime-int)
132 long sz = sysconf(_SC_PAGESIZE);
133 LOG_IF(sz == -1, FATAL, RUNTIME) << "Can't get page size from OS";
134 return static_cast<uint32_t>(sz);
135 }
136
GetPageSize()137 uint32_t GetPageSize()
138 {
139 // NOLINTNEXTLINE(google-runtime-int)
140 static uint32_t sz = GetPageSizeFromOs();
141 return sz;
142 }
143
MapRWAnonymousRaw(size_t size,bool force_poison)144 void *MapRWAnonymousRaw(size_t size, bool force_poison)
145 {
146 ASSERT(size % GetPageSize() == 0);
147 // NOLINTNEXTLINE(hicpp-signed-bitwise)
148 void *result = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
149 if (result == reinterpret_cast<void *>(-1)) {
150 result = nullptr;
151 }
152 if ((result != nullptr) && force_poison) {
153 ASAN_POISON_MEMORY_REGION(result, size);
154 }
155
156 return result;
157 }
158
PartiallyUnmapRaw(void * mem,size_t size)159 std::optional<Error> PartiallyUnmapRaw(void *mem, size_t size)
160 {
161 // We can partially unmap memory on Unix systems via common unmap
162 return UnmapRaw(mem, size);
163 }
164
MapRWAnonymousWithAlignmentRaw(size_t size,size_t aligment_in_bytes,bool force_poison)165 void *MapRWAnonymousWithAlignmentRaw(size_t size, size_t aligment_in_bytes, bool force_poison)
166 {
167 ASSERT(aligment_in_bytes % GetPageSize() == 0);
168 if (size == 0) {
169 return nullptr;
170 }
171 void *result = MapRWAnonymousRaw(size + aligment_in_bytes, force_poison);
172 if (result == nullptr) {
173 return result;
174 }
175 auto allocated_mem = reinterpret_cast<uintptr_t>(result);
176 uintptr_t aligned_mem = (allocated_mem & ~(aligment_in_bytes - 1U)) +
177 ((allocated_mem % aligment_in_bytes) != 0U ? aligment_in_bytes : 0U);
178 ASSERT(aligned_mem >= allocated_mem);
179 size_t unused_in_start = aligned_mem - allocated_mem;
180 ASSERT(unused_in_start <= aligment_in_bytes);
181 size_t unused_in_end = aligment_in_bytes - unused_in_start;
182 if (unused_in_start != 0) {
183 PartiallyUnmapRaw(result, unused_in_start);
184 }
185 if (unused_in_end != 0) {
186 auto end_part = reinterpret_cast<void *>(aligned_mem + size);
187 PartiallyUnmapRaw(end_part, unused_in_end);
188 }
189 return reinterpret_cast<void *>(aligned_mem);
190 }
191
MapRWAnonymousFixedRaw(void * mem,size_t size,bool force_poison)192 void *MapRWAnonymousFixedRaw(void *mem, size_t size, bool force_poison)
193 {
194 #if defined(PANDA_ASAN_ON)
195 // If this assert fails, please decrease the size of the memory for you program
196 // or don't run it with ASAN.
197 if (!((reinterpret_cast<uintptr_t>(mem) > MMAP_FIXED_MAGIC_ADDR_FOR_ASAN) ||
198 ((reinterpret_cast<uintptr_t>(mem) + size) < MMAP_FIXED_MAGIC_ADDR_FOR_ASAN))) {
199 ASSERT((reinterpret_cast<uintptr_t>(mem) > MMAP_FIXED_MAGIC_ADDR_FOR_ASAN) ||
200 ((reinterpret_cast<uintptr_t>(mem) + size) < MMAP_FIXED_MAGIC_ADDR_FOR_ASAN));
201 std::abort();
202 }
203 #endif
204 ASSERT(size % GetPageSize() == 0);
205 void *result = // NOLINTNEXTLINE(hicpp-signed-bitwise)
206 mmap(mem, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
207 if (result == reinterpret_cast<void *>(-1)) {
208 result = nullptr;
209 }
210 if ((result != nullptr) && force_poison) {
211 // If you have such an error here:
212 // ==4120==AddressSanitizer CHECK failed:
213 // ../../../../src/libsanitizer/asan/asan_mapping.h:303 "((AddrIsInMem(p))) != (0)" (0x0, 0x0)
214 // Look at the big comment at the start of the method.
215 ASAN_POISON_MEMORY_REGION(result, size);
216 }
217
218 return result;
219 }
220
UnmapRaw(void * mem,size_t size)221 std::optional<Error> UnmapRaw(void *mem, size_t size)
222 {
223 ASAN_UNPOISON_MEMORY_REGION(mem, size);
224 int res = munmap(mem, size);
225 if (UNLIKELY(res == -1)) {
226 return Error(errno);
227 }
228
229 return {};
230 }
231
232 #ifdef PANDA_TARGET_MOBILE
233 #include <sys/prctl.h>
234
235 #ifndef PR_SET_VMA
236 constexpr int PR_SET_VMA = 0x53564d41;
237 #endif
238
239 #ifndef PR_SET_VMA_ANON_NAME
240 constexpr unsigned long PR_SET_VMA_ANON_NAME = 0;
241 #endif
242 #endif // PANDA_TARGET_MOBILE
243
TagAnonymousMemory(const void * mem,size_t size,const char * tag)244 std::optional<Error> TagAnonymousMemory([[maybe_unused]] const void *mem, [[maybe_unused]] size_t size,
245 [[maybe_unused]] const char *tag)
246 {
247 #ifdef PANDA_TARGET_MOBILE
248 ASSERT(size % GetPageSize() == 0);
249 ASSERT(reinterpret_cast<uintptr_t>(mem) % GetPageSize() == 0);
250
251 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
252 int res = prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
253 // NOLINTNEXTLINE(google-runtime-int)
254 static_cast<unsigned long>(ToUintPtr(mem)), size,
255 // NOLINTNEXTLINE(google-runtime-int)
256 static_cast<unsigned long>(ToUintPtr(tag)));
257 if (UNLIKELY(res == -1)) {
258 return Error(errno);
259 }
260 #endif // PANDA_TARGET_MOBILE
261 return {};
262 }
263
GetNativeBytesFromMallinfo()264 size_t GetNativeBytesFromMallinfo()
265 {
266 size_t mallinfo_bytes;
267 #if defined(PANDA_ASAN_ON) || defined(PANDA_TSAN_ON)
268 mallinfo_bytes = DEFAULT_NATIVE_BYTES_FROM_MALLINFO;
269 LOG(INFO, RUNTIME) << "Get native bytes from mallinfo with ASAN or TSAN. Return default value";
270 #else
271 #if defined(__GLIBC__) || defined(PANDA_TARGET_MOBILE)
272
273 // For GLIBC, uordblks is total size of space which is allocated by malloc
274 // For mobile libc, uordblks is total size of space which is allocated by malloc or mmap called by malloc for
275 // non-small allocations
276 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 33
277 struct mallinfo2 info = mallinfo2();
278 mallinfo_bytes = info.uordblks;
279 #else
280 struct mallinfo info = mallinfo();
281 mallinfo_bytes = static_cast<unsigned int>(info.uordblks);
282 #endif // __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 33
283
284 #if defined(__GLIBC__)
285
286 // For GLIBC, hblkhd is total size of space which is allocated by mmap called by malloc for non-small allocations
287 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 33
288 mallinfo_bytes += info.hblkhd;
289 #else
290 mallinfo_bytes += static_cast<unsigned int>(info.hblkhd);
291 #endif // __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 33
292
293 #endif // __GLIBC__
294 #else
295 mallinfo_bytes = DEFAULT_NATIVE_BYTES_FROM_MALLINFO;
296 LOG(INFO, RUNTIME) << "Get native bytes from mallinfo without GLIBC or mobile libc. Return default value";
297 #endif // __GLIBC__ || PANDA_TARGET_MOBILE
298 #endif // PANDA_ASAN_ON || PANDA_TSAN_ON
299 // For ASAN or TSAN, return default value. For GLIBC, return uordblks + hblkhd. For mobile libc, return uordblks.
300 // For other, return default value.
301 return mallinfo_bytes;
302 }
303
304 } // namespace panda::os::mem
305