1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "wtf/PageAllocator.h" 33 34 #include "wtf/AddressSpaceRandomization.h" 35 #include "wtf/Assertions.h" 36 37 #include <limits.h> 38 39 #if OS(POSIX) 40 41 #include <sys/mman.h> 42 43 #ifndef MADV_FREE 44 #define MADV_FREE MADV_DONTNEED 45 #endif 46 47 #ifndef MAP_ANONYMOUS 48 #define MAP_ANONYMOUS MAP_ANON 49 #endif 50 51 #elif OS(WIN) 52 53 #include <windows.h> 54 55 #else 56 #error Unknown OS 57 #endif // OS(POSIX) 58 59 namespace WTF { 60 61 #if OS(WIN) 62 63 static bool shouldUseAddressHint() 64 { 65 #if CPU(32BIT) 66 // When running 32-bit processes under 32-bit Windows, the userspace is 67 // limited to 2 GB, and we risk fragmenting it badly if we allow further 68 // randomization via our address hint. On the other hand, if the process 69 // is running under WOW64, then it has at least 3 GB available (and likely 70 // 4 GB depending upon the OS version), and we want use the additional 71 // randomness. 72 static BOOL bIsWow64 = -1; 73 if (bIsWow64 == -1) { 74 IsWow64Process(GetCurrentProcess(), &bIsWow64); 75 } 76 return !!bIsWow64; 77 #else // CPU(32BIT) 78 return true; 79 #endif // CPU(32BIT) 80 } 81 82 #endif // OS(WIN) 83 84 // This simple internal function wraps the OS-specific page allocation call so 85 // that it behaves consistently: the address is a hint and if it cannot be used, 86 // the allocation will be placed elsewhere. 87 static void* systemAllocPages(void* addr, size_t len) 88 { 89 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); 90 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffsetMask)); 91 void* ret = 0; 92 #if OS(WIN) 93 if (shouldUseAddressHint()) 94 ret = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 95 if (!ret) 96 ret = VirtualAlloc(0, len, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 97 #else 98 ret = mmap(addr, len, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 99 if (ret == MAP_FAILED) 100 ret = 0; 101 #endif 102 return ret; 103 } 104 105 static bool trimMapping(void* baseAddr, size_t baseLen, void* trimAddr, size_t trimLen) 106 { 107 #if OS(WIN) 108 return false; 109 #else 110 char* basePtr = static_cast<char*>(baseAddr); 111 char* trimPtr = static_cast<char*>(trimAddr); 112 ASSERT(trimPtr >= basePtr); 113 ASSERT(trimPtr + trimLen <= basePtr + baseLen); 114 size_t preLen = trimPtr - basePtr; 115 if (preLen) { 116 int ret = munmap(basePtr, preLen); 117 RELEASE_ASSERT(!ret); 118 } 119 size_t postLen = (basePtr + baseLen) - (trimPtr + trimLen); 120 if (postLen) { 121 int ret = munmap(trimPtr + trimLen, postLen); 122 RELEASE_ASSERT(!ret); 123 } 124 return true; 125 #endif 126 } 127 128 void* allocPages(void* addr, size_t len, size_t align) 129 { 130 ASSERT(len >= kPageAllocationGranularity); 131 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); 132 ASSERT(align >= kPageAllocationGranularity); 133 ASSERT(!(align & kPageAllocationGranularityOffsetMask)); 134 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffsetMask)); 135 size_t alignOffsetMask = align - 1; 136 size_t alignBaseMask = ~alignOffsetMask; 137 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & alignOffsetMask)); 138 // If the client passed null as the address, choose a good one. 139 if (!addr) { 140 addr = getRandomPageBase(); 141 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & alignBaseMask); 142 } 143 144 // The common case, which is also the least work we can do, is that the 145 // address and length are suitable. Just try it. 146 void* ret = systemAllocPages(addr, len); 147 // If the alignment is to our liking, we're done. 148 if (!ret || !(reinterpret_cast<uintptr_t>(ret) & alignOffsetMask)) 149 return ret; 150 151 // Annoying. Unmap and map a larger range to be sure to succeed on the 152 // second, slower attempt. 153 freePages(ret, len); 154 155 size_t tryLen = len + (align - kPageAllocationGranularity); 156 RELEASE_ASSERT(tryLen > len); 157 158 // We loop to cater for the unlikely case where another thread maps on top 159 // of the aligned location we choose. 160 int count = 0; 161 while (count++ < 100) { 162 ret = systemAllocPages(addr, tryLen); 163 if (!ret) 164 return 0; 165 // We can now try and trim out a subset of the mapping. 166 addr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(ret) + alignOffsetMask) & alignBaseMask); 167 168 // On POSIX systems, we can trim the oversized mapping to fit exactly. 169 // This will always work on POSIX systems. 170 if (trimMapping(ret, tryLen, addr, len)) 171 return addr; 172 173 // On Windows, you can't trim an existing mapping so we unmap and remap 174 // a subset. We used to do for all platforms, but OSX 10.8 has a 175 // broken mmap() that ignores address hints for valid, unused addresses. 176 freePages(ret, tryLen); 177 ret = systemAllocPages(addr, len); 178 if (ret == addr || !ret) 179 return ret; 180 181 // Unlikely race / collision. Do the simple thing and just start again. 182 freePages(ret, len); 183 addr = getRandomPageBase(); 184 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & alignBaseMask); 185 } 186 IMMEDIATE_CRASH(); 187 return 0; 188 } 189 190 void freePages(void* addr, size_t len) 191 { 192 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffsetMask)); 193 ASSERT(!(len & kPageAllocationGranularityOffsetMask)); 194 #if OS(POSIX) 195 int ret = munmap(addr, len); 196 RELEASE_ASSERT(!ret); 197 #else 198 BOOL ret = VirtualFree(addr, 0, MEM_RELEASE); 199 RELEASE_ASSERT(ret); 200 #endif 201 } 202 203 void setSystemPagesInaccessible(void* addr, size_t len) 204 { 205 ASSERT(!(len & kSystemPageOffsetMask)); 206 #if OS(POSIX) 207 int ret = mprotect(addr, len, PROT_NONE); 208 RELEASE_ASSERT(!ret); 209 #else 210 BOOL ret = VirtualFree(addr, len, MEM_DECOMMIT); 211 RELEASE_ASSERT(ret); 212 #endif 213 } 214 215 void setSystemPagesAccessible(void* addr, size_t len) 216 { 217 ASSERT(!(len & kSystemPageOffsetMask)); 218 #if OS(POSIX) 219 int ret = mprotect(addr, len, PROT_READ | PROT_WRITE); 220 RELEASE_ASSERT(!ret); 221 #else 222 void* ret = VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE); 223 RELEASE_ASSERT(ret); 224 #endif 225 } 226 227 void decommitSystemPages(void* addr, size_t len) 228 { 229 ASSERT(!(len & kSystemPageOffsetMask)); 230 #if OS(POSIX) 231 int ret = madvise(addr, len, MADV_FREE); 232 RELEASE_ASSERT(!ret); 233 #else 234 setSystemPagesInaccessible(addr, len); 235 #endif 236 } 237 238 void recommitSystemPages(void* addr, size_t len) 239 { 240 ASSERT(!(len & kSystemPageOffsetMask)); 241 #if OS(POSIX) 242 (void) addr; 243 #else 244 setSystemPagesAccessible(addr, len); 245 #endif 246 } 247 248 } // namespace WTF 249 250