1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/portability/SysMman.h>
18 
19 #ifdef _WIN32
20 
21 #include <cassert>
22 
23 #include <folly/Portability.h>
24 #include <folly/portability/Windows.h>
25 
mmap_to_page_protection(int prot,DWORD & ret,DWORD & acc)26 static bool mmap_to_page_protection(int prot, DWORD& ret, DWORD& acc) {
27   if (prot == PROT_NONE) {
28     ret = PAGE_NOACCESS;
29     acc = 0;
30   } else if (prot == PROT_READ) {
31     ret = PAGE_READONLY;
32     acc = FILE_MAP_READ;
33   } else if (prot == PROT_EXEC) {
34     ret = PAGE_EXECUTE;
35     acc = FILE_MAP_EXECUTE;
36   } else if (prot == (PROT_READ | PROT_EXEC)) {
37     ret = PAGE_EXECUTE_READ;
38     acc = FILE_MAP_READ | FILE_MAP_EXECUTE;
39   } else if (prot == (PROT_READ | PROT_WRITE)) {
40     ret = PAGE_READWRITE;
41     acc = FILE_MAP_READ | FILE_MAP_WRITE;
42   } else if (prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
43     ret = PAGE_EXECUTE_READWRITE;
44     acc = FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE;
45   } else {
46     return false;
47   }
48   return true;
49 }
50 
alignToAllocationGranularity(size_t s)51 static size_t alignToAllocationGranularity(size_t s) {
52   static size_t granularity = [] {
53     static SYSTEM_INFO inf;
54     GetSystemInfo(&inf);
55     return inf.dwAllocationGranularity;
56   }();
57   return (s + granularity - 1) / granularity * granularity;
58 }
59 
60 extern "C" {
madvise(const void *,size_t,int)61 int madvise(const void* /* addr */, size_t /* len */, int /* advise */) {
62   // We do nothing at all.
63   // Could probably implement dontneed via VirtualAlloc
64   // with the MEM_RESET and MEM_RESET_UNDO flags.
65   return 0;
66 }
67 
mlock(const void * addr,size_t len)68 int mlock(const void* addr, size_t len) {
69   // For some strange reason, it's allowed to
70   // lock a nullptr as long as length is zero.
71   // VirtualLock doesn't allow it, so handle
72   // it specially.
73   if (addr == nullptr && len == 0) {
74     return 0;
75   }
76   if (!VirtualLock((void*)addr, len)) {
77     return -1;
78   }
79   return 0;
80 }
81 
82 namespace {
83 constexpr uint32_t kMMapLengthMagic = 0xFACEB00C;
84 struct MemMapDebugTrailer {
85   size_t length;
86   uint32_t magic;
87 };
88 } // namespace
89 
mmap(void * addr,size_t length,int prot,int flags,int fd,off_t off)90 void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
91   // Make sure it's something we support first.
92 
93   // No Anon shared.
94   if ((flags & (MAP_ANONYMOUS | MAP_SHARED)) == (MAP_ANONYMOUS | MAP_SHARED)) {
95     return MAP_FAILED;
96   }
97   // No private copy on write.
98   // If the map isn't writable, we can let it go through as
99   // whether changes to the underlying file are reflected in the map
100   // is defined to be unspecified by the standard.
101   if ((flags & MAP_PRIVATE) == MAP_PRIVATE &&
102       (prot & PROT_WRITE) == PROT_WRITE && fd != -1) {
103     return MAP_FAILED;
104   }
105   // Map isn't anon, must be file backed.
106   if (!(flags & MAP_ANONYMOUS) && fd == -1) {
107     return MAP_FAILED;
108   }
109 
110   DWORD newProt;
111   DWORD accessFlags;
112   if (!mmap_to_page_protection(prot, newProt, accessFlags)) {
113     return MAP_FAILED;
114   }
115 
116   void* ret;
117   if (!(flags & MAP_ANONYMOUS) || (flags & MAP_SHARED)) {
118     HANDLE h = INVALID_HANDLE_VALUE;
119     if (!(flags & MAP_ANONYMOUS)) {
120       h = (HANDLE)_get_osfhandle(fd);
121     }
122 
123     HANDLE fmh = CreateFileMapping(
124         h,
125         nullptr,
126         newProt,
127         (DWORD)((length >> 32) & 0xFFFFFFFF),
128         (DWORD)(length & 0xFFFFFFFF),
129         nullptr);
130     if (fmh == nullptr) {
131       return MAP_FAILED;
132     }
133     ret = MapViewOfFileEx(
134         fmh,
135         accessFlags,
136         (DWORD)(0), // off_t is only 32-bit :(
137         (DWORD)(off & 0xFFFFFFFF),
138         0,
139         addr);
140     if (ret == nullptr) {
141       ret = MAP_FAILED;
142     }
143     CloseHandle(fmh);
144   } else {
145     auto baseLength = length;
146     if (folly::kIsDebug) {
147       // In debug mode we keep track of the length to make
148       // sure you're only munmapping the entire thing if
149       // we're using VirtualAlloc.
150       length += sizeof(MemMapDebugTrailer);
151     }
152 
153     // VirtualAlloc rounds size down to a multiple
154     // of the system allocation granularity :(
155     length = alignToAllocationGranularity(length);
156     ret = VirtualAlloc(addr, length, MEM_COMMIT | MEM_RESERVE, newProt);
157     if (ret == nullptr) {
158       return MAP_FAILED;
159     }
160 
161     if (folly::kIsDebug) {
162       auto deb = (MemMapDebugTrailer*)((char*)ret + baseLength);
163       deb->length = baseLength;
164       deb->magic = kMMapLengthMagic;
165     }
166   }
167 
168   // TODO: Could technically implement MAP_POPULATE via PrefetchVirtualMemory
169   //       Should also see about implementing MAP_NORESERVE
170   return ret;
171 }
172 
mprotect(void * addr,size_t size,int prot)173 int mprotect(void* addr, size_t size, int prot) {
174   DWORD newProt;
175   DWORD access;
176   if (!mmap_to_page_protection(prot, newProt, access)) {
177     return -1;
178   }
179 
180   DWORD oldProt;
181   BOOL res = VirtualProtect(addr, size, newProt, &oldProt);
182   if (!res) {
183     return -1;
184   }
185   return 0;
186 }
187 
munlock(const void * addr,size_t length)188 int munlock(const void* addr, size_t length) {
189   // See comment in mlock
190   if (addr == nullptr && length == 0) {
191     return 0;
192   }
193   if (!VirtualUnlock((void*)addr, length)) {
194     return -1;
195   }
196   return 0;
197 }
198 
munmap(void * addr,size_t length)199 int munmap(void* addr, size_t length) {
200   // Try to unmap it as a file, otherwise VirtualFree.
201   if (!UnmapViewOfFile(addr)) {
202     if (folly::kIsDebug) {
203       // We can't do partial unmapping with Windows, so
204       // assert that we aren't trying to do that if we're
205       // in debug mode.
206       MEMORY_BASIC_INFORMATION inf;
207       VirtualQuery(addr, &inf, sizeof(inf));
208       assert(inf.AllocationBase == addr);
209 
210       auto deb = (MemMapDebugTrailer*)((char*)addr + length);
211       assert(deb->length == length);
212       assert(deb->magic == kMMapLengthMagic);
213     }
214     if (!VirtualFree(addr, 0, MEM_RELEASE)) {
215       return -1;
216     }
217     return 0;
218   }
219   return 0;
220 }
221 }
222 
223 #endif
224