1 /*
2 * Copyright (c) 2021 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 "windows_mem.h"
18 #include "utils/type_helpers.h"
19 #include "utils/asan_interface.h"
20
21 #include <limits>
22
23 #include <windows.h>
24 #include <errno.h>
25 #include <io.h>
26
27 #include <type_traits>
28
29 #define MAP_FAILED (reinterpret_cast<void *>(-1))
30
31 namespace panda::os::mem {
32
mem_errno(const DWORD err,const int deferr)33 static int mem_errno(const DWORD err, const int deferr)
34 {
35 return err == 0 ? deferr : err;
36 }
37
mem_protection_flags_for_page(const int prot)38 static DWORD mem_protection_flags_for_page(const int prot)
39 {
40 DWORD flags = 0;
41 if (static_cast<unsigned>(prot) == MMAP_PROT_NONE) {
42 return flags;
43 }
44
45 if ((static_cast<unsigned>(prot) & MMAP_PROT_EXEC) != 0) {
46 flags = ((static_cast<unsigned>(prot) & MMAP_PROT_WRITE) != 0) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
47 } else {
48 flags = ((static_cast<unsigned>(prot) & MMAP_PROT_WRITE) != 0) ? PAGE_READWRITE : PAGE_READONLY;
49 }
50
51 return flags;
52 }
53
mem_protection_flags_for_file(const int prot)54 static DWORD mem_protection_flags_for_file(const int prot)
55 {
56 DWORD flags = 0;
57
58 if (prot == MMAP_PROT_NONE) {
59 return flags;
60 }
61
62 if ((static_cast<unsigned>(prot) & MMAP_PROT_READ) != 0) {
63 flags |= FILE_MAP_READ;
64 }
65 if ((static_cast<unsigned>(prot) & MMAP_PROT_WRITE) != 0) {
66 flags |= FILE_MAP_WRITE;
67 }
68 if ((static_cast<unsigned>(prot) & MMAP_PROT_EXEC) != 0) {
69 flags |= FILE_MAP_EXECUTE;
70 }
71
72 return flags;
73 }
74
mem_select_lower_bound(off_t off)75 static DWORD mem_select_lower_bound(off_t off)
76 {
77 using uoff_t = std::make_unsigned_t<off_t>;
78 return (sizeof(off_t) <= sizeof(DWORD)) ? static_cast<DWORD>(off)
79 : static_cast<DWORD>(static_cast<uoff_t>(off) & 0xFFFFFFFFL);
80 }
81
mem_select_upper_bound(off_t off)82 static DWORD mem_select_upper_bound(off_t off)
83 {
84 constexpr uint32_t OFFSET_DWORD = 32;
85 using uoff_t = std::make_unsigned_t<off_t>;
86 return (sizeof(off_t) <= sizeof(DWORD))
87 ? static_cast<DWORD>(0)
88 : static_cast<DWORD>((static_cast<uoff_t>(off) >> OFFSET_DWORD) & 0xFFFFFFFFL);
89 }
90
mmap(void * addr,size_t len,int prot,uint32_t flags,int fildes,off_t off)91 void *mmap([[maybe_unused]] void *addr, size_t len, int prot, uint32_t flags, int fildes, off_t off)
92 {
93 errno = 0;
94
95 // Skip unsupported combinations of flags:
96 if (len == 0 || (flags & MMAP_FLAG_FIXED) != 0 || prot == MMAP_PROT_EXEC) {
97 errno = EINVAL;
98 return MAP_FAILED;
99 }
100
101 HANDLE h =
102 ((flags & MMAP_FLAG_ANONYMOUS) == 0) ? reinterpret_cast<HANDLE>(_get_osfhandle(fildes)) : INVALID_HANDLE_VALUE;
103 if ((flags & MMAP_FLAG_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) {
104 errno = EBADF;
105 return MAP_FAILED;
106 }
107
108 const auto prot_page = mem_protection_flags_for_page(prot);
109 const off_t max_size = off + static_cast<off_t>(len);
110 const auto max_size_low = mem_select_lower_bound(max_size);
111 const auto max_size_high = mem_select_upper_bound(max_size);
112 HANDLE fm = CreateFileMapping(h, nullptr, prot_page, max_size_high, max_size_low, nullptr);
113 if (fm == nullptr) {
114 errno = mem_errno(GetLastError(), EPERM);
115 return MAP_FAILED;
116 }
117
118 const auto prot_file = mem_protection_flags_for_file(prot);
119 const auto file_off_low = mem_select_lower_bound(off);
120 const auto file_off_high = mem_select_upper_bound(off);
121 void *map = MapViewOfFile(fm, prot_file, file_off_high, file_off_low, len);
122 CloseHandle(fm);
123 if (map == nullptr) {
124 errno = mem_errno(GetLastError(), EPERM);
125 return MAP_FAILED;
126 }
127
128 return map;
129 }
130
munmap(void * addr,size_t len)131 int munmap(void *addr, [[maybe_unused]] size_t len)
132 {
133 if (UnmapViewOfFile(addr)) {
134 return 0;
135 }
136
137 errno = mem_errno(GetLastError(), EPERM);
138 return -1;
139 }
140
MmapDeleter(std::byte * ptr,size_t size)141 void MmapDeleter(std::byte *ptr, size_t size) noexcept
142 {
143 if (ptr != nullptr) {
144 munmap(ptr, size);
145 }
146 }
147
MapFile(file::File file,uint32_t prot,uint32_t flags,size_t size,size_t file_offset,void * hint)148 BytePtr MapFile(file::File file, uint32_t prot, uint32_t flags, size_t size, size_t file_offset, void *hint)
149 {
150 size_t map_offset = RoundDown(file_offset, GetPageSize());
151 size_t offset = file_offset - map_offset;
152 size_t map_size = size + offset;
153 void *result = mmap(hint, map_size, prot, flags, file.GetFd(), map_offset);
154 if (UNLIKELY(result == MAP_FAILED)) {
155 return BytePtr(nullptr, 0, MmapDeleter);
156 }
157
158 return BytePtr(static_cast<std::byte *>(result) + offset, size, MmapDeleter);
159 }
160
GetPageSize()161 uint32_t GetPageSize()
162 {
163 constexpr size_t PAGE_SIZE = 4096;
164 return PAGE_SIZE;
165 }
166
MapRWAnonymousRaw(size_t size,bool force_poison)167 void *MapRWAnonymousRaw(size_t size, bool force_poison)
168 {
169 ASSERT(size % GetPageSize() == 0);
170 // NOLINTNEXTLINE(hicpp-signed-bitwise)
171 void *result =
172 mmap(nullptr, size, MMAP_PROT_READ | MMAP_PROT_WRITE, MMAP_FLAG_PRIVATE | MMAP_FLAG_ANONYMOUS, -1, 0);
173 if (UNLIKELY(result == MAP_FAILED)) {
174 result = nullptr;
175 }
176 if ((result != nullptr) && force_poison) {
177 ASAN_POISON_MEMORY_REGION(result, size);
178 }
179
180 return result;
181 }
182
MapRWAnonymousWithAlignmentRaw(size_t size,size_t aligment_in_bytes,bool force_poison)183 void *MapRWAnonymousWithAlignmentRaw(size_t size, size_t aligment_in_bytes, bool force_poison)
184 {
185 ASSERT(aligment_in_bytes % GetPageSize() == 0);
186 if (size == 0) {
187 return nullptr;
188 }
189 void *result = MapRWAnonymousRaw(size + aligment_in_bytes, force_poison);
190 if (result == nullptr) {
191 return result;
192 }
193 auto allocated_mem = reinterpret_cast<uintptr_t>(result);
194 uintptr_t aligned_mem = (allocated_mem & ~(aligment_in_bytes - 1U)) +
195 ((allocated_mem % aligment_in_bytes) != 0U ? aligment_in_bytes : 0U);
196 ASSERT(aligned_mem >= allocated_mem);
197 size_t unused_in_start = aligned_mem - allocated_mem;
198 ASSERT(unused_in_start <= aligment_in_bytes);
199 size_t unused_in_end = aligment_in_bytes - unused_in_start;
200 if (unused_in_start != 0) {
201 UnmapRaw(result, unused_in_start);
202 }
203 if (unused_in_end != 0) {
204 auto end_part = reinterpret_cast<void *>(aligned_mem + size);
205 UnmapRaw(end_part, unused_in_end);
206 }
207 return reinterpret_cast<void *>(aligned_mem);
208 }
209
AlignedAlloc(size_t alignment_in_bytes,size_t size)210 void *AlignedAlloc(size_t alignment_in_bytes, size_t size)
211 {
212 size_t aligned_size = (size + alignment_in_bytes - 1) & ~(alignment_in_bytes - 1);
213 // Aligned_alloc is not supported on MingW. We need to call _aligned_malloc instead.
214 auto ret = _aligned_malloc(aligned_size, alignment_in_bytes);
215 // _aligned_malloc returns aligned pointer so just add assertion, no need to do runtime checks
216 ASSERT(reinterpret_cast<uintptr_t>(ret) == (reinterpret_cast<uintptr_t>(ret) & ~(alignment_in_bytes - 1)));
217 return ret;
218 }
219
UnmapRaw(void * mem,size_t size)220 std::optional<Error> UnmapRaw(void *mem, size_t size)
221 {
222 ASAN_UNPOISON_MEMORY_REGION(mem, size);
223 int res = munmap(mem, size);
224 if (UNLIKELY(res == -1)) {
225 return Error(errno);
226 }
227
228 return {};
229 }
230
TagAnonymousMemory(const void * mem,size_t size,const char * tag)231 std::optional<Error> TagAnonymousMemory([[maybe_unused]] const void *mem, [[maybe_unused]] size_t size,
232 [[maybe_unused]] const char *tag)
233 {
234 return {};
235 }
236
237 } // namespace panda::os::mem
238