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 59namespace WTF { 60 61#if OS(WIN) 62 63static 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. 87static 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 105static 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 128void* 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 190void 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 203void 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 215void 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 227void 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 238void 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