• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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