| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/allocator/partition_allocator/page_allocator.h" | 5 #include "base/allocator/partition_allocator/page_allocator.h" |
| 6 | 6 |
| 7 #include <limits.h> | 7 #include <limits.h> |
| 8 | 8 |
| 9 #include "base/allocator/partition_allocator/address_space_randomization.h" | 9 #include "base/allocator/partition_allocator/address_space_randomization.h" |
| 10 #include "base/atomicops.h" | 10 #include "base/atomicops.h" |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 static base::subtle::Atomic32 s_allocPageErrorCode = ERROR_SUCCESS; | 38 static base::subtle::Atomic32 s_allocPageErrorCode = ERROR_SUCCESS; |
| 39 | 39 |
| 40 #else | 40 #else |
| 41 #error Unknown OS | 41 #error Unknown OS |
| 42 #endif // defined(OS_POSIX) | 42 #endif // defined(OS_POSIX) |
| 43 | 43 |
| 44 namespace base { | 44 namespace base { |
| 45 | 45 |
| 46 // This internal function wraps the OS-specific page allocation call: | 46 // This internal function wraps the OS-specific page allocation call: |
| 47 // |VirtualAlloc| on Windows, and |mmap| on POSIX. | 47 // |VirtualAlloc| on Windows, and |mmap| on POSIX. |
| 48 static void* systemAllocPages( | 48 static void* SystemAllocPages( |
| 49 void* hint, | 49 void* hint, |
| 50 size_t len, | 50 size_t length, |
| 51 PageAccessibilityConfiguration pageAccessibility) { | 51 PageAccessibilityConfiguration page_accessibility) { |
| 52 DCHECK(!(len & kPageAllocationGranularityOffsetMask)); | 52 DCHECK(!(length & kPageAllocationGranularityOffsetMask)); |
| 53 DCHECK(!(reinterpret_cast<uintptr_t>(hint) & | 53 DCHECK(!(reinterpret_cast<uintptr_t>(hint) & |
| 54 kPageAllocationGranularityOffsetMask)); | 54 kPageAllocationGranularityOffsetMask)); |
| 55 void* ret; | 55 void* ret; |
| 56 #if defined(OS_WIN) | 56 #if defined(OS_WIN) |
| 57 DWORD accessFlag = | 57 DWORD access_flag = |
| 58 pageAccessibility == PageAccessible ? PAGE_READWRITE : PAGE_NOACCESS; | 58 page_accessibility == PageAccessible ? PAGE_READWRITE : PAGE_NOACCESS; |
| 59 ret = VirtualAlloc(hint, len, MEM_RESERVE | MEM_COMMIT, accessFlag); | 59 ret = VirtualAlloc(hint, length, MEM_RESERVE | MEM_COMMIT, access_flag); |
| 60 if (!ret) | 60 if (!ret) |
| 61 base::subtle::Release_Store(&s_allocPageErrorCode, GetLastError()); | 61 base::subtle::Release_Store(&s_allocPageErrorCode, GetLastError()); |
| 62 #else | 62 #else |
| 63 int accessFlag = pageAccessibility == PageAccessible | 63 int access_flag = page_accessibility == PageAccessible |
| 64 ? (PROT_READ | PROT_WRITE) | 64 ? (PROT_READ | PROT_WRITE) |
| 65 : PROT_NONE; | 65 : PROT_NONE; |
| 66 ret = mmap(hint, len, accessFlag, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); | 66 ret = mmap(hint, length, access_flag, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
| 67 if (ret == MAP_FAILED) { | 67 if (ret == MAP_FAILED) { |
| 68 base::subtle::Release_Store(&s_allocPageErrorCode, errno); | 68 base::subtle::Release_Store(&s_allocPageErrorCode, errno); |
| 69 ret = 0; | 69 ret = 0; |
| 70 } | 70 } |
| 71 #endif | 71 #endif |
| 72 return ret; | 72 return ret; |
| 73 } | 73 } |
| 74 | 74 |
| 75 // Trims base to given length and alignment. Windows returns null on failure and | 75 // Trims base to given length and alignment. Windows returns null on failure and |
| 76 // frees base. | 76 // frees base. |
| 77 static void* trimMapping(void* base, | 77 static void* TrimMapping(void* base, |
| 78 size_t baseLen, | 78 size_t base_length, |
| 79 size_t trimLen, | 79 size_t trim_length, |
| 80 uintptr_t align, | 80 uintptr_t align, |
| 81 PageAccessibilityConfiguration pageAccessibility) { | 81 PageAccessibilityConfiguration page_accessibility) { |
| 82 size_t preSlack = reinterpret_cast<uintptr_t>(base) & (align - 1); | 82 size_t pre_slack = reinterpret_cast<uintptr_t>(base) & (align - 1); |
| 83 if (preSlack) | 83 if (pre_slack) |
| 84 preSlack = align - preSlack; | 84 pre_slack = align - pre_slack; |
| 85 size_t postSlack = baseLen - preSlack - trimLen; | 85 size_t post_slack = base_length - pre_slack - trim_length; |
| 86 DCHECK(baseLen >= trimLen || preSlack || postSlack); | 86 DCHECK(base_length >= trim_length || pre_slack || post_slack); |
| 87 DCHECK(preSlack < baseLen); | 87 DCHECK(pre_slack < base_length); |
| 88 DCHECK(postSlack < baseLen); | 88 DCHECK(post_slack < base_length); |
| 89 void* ret = base; | 89 void* ret = base; |
| 90 | 90 |
| 91 #if defined(OS_POSIX) // On POSIX we can resize the allocation run. | 91 #if defined(OS_POSIX) // On POSIX we can resize the allocation run. |
| 92 (void)pageAccessibility; | 92 (void)page_accessibility; |
| 93 if (preSlack) { | 93 if (pre_slack) { |
| 94 int res = munmap(base, preSlack); | 94 int res = munmap(base, pre_slack); |
| 95 CHECK(!res); | 95 CHECK(!res); |
| 96 ret = reinterpret_cast<char*>(base) + preSlack; | 96 ret = reinterpret_cast<char*>(base) + pre_slack; |
| 97 } | 97 } |
| 98 if (postSlack) { | 98 if (post_slack) { |
| 99 int res = munmap(reinterpret_cast<char*>(ret) + trimLen, postSlack); | 99 int res = munmap(reinterpret_cast<char*>(ret) + trim_length, post_slack); |
| 100 CHECK(!res); | 100 CHECK(!res); |
| 101 } | 101 } |
| 102 #else // On Windows we can't resize the allocation run. | 102 #else // On Windows we can't resize the allocation run. |
| 103 if (preSlack || postSlack) { | 103 if (pre_slack || post_slack) { |
| 104 ret = reinterpret_cast<char*>(base) + preSlack; | 104 ret = reinterpret_cast<char*>(base) + pre_slack; |
| 105 freePages(base, baseLen); | 105 FreePages(base, base_length); |
| 106 ret = systemAllocPages(ret, trimLen, pageAccessibility); | 106 ret = SystemAllocPages(ret, trim_length, page_accessibility); |
| 107 } | 107 } |
| 108 #endif | 108 #endif |
| 109 | 109 |
| 110 return ret; | 110 return ret; |
| 111 } | 111 } |
| 112 | 112 |
| 113 void* allocPages(void* addr, | 113 void* AllocPages(void* address, |
| 114 size_t len, | 114 size_t length, |
| 115 size_t align, | 115 size_t align, |
| 116 PageAccessibilityConfiguration pageAccessibility) { | 116 PageAccessibilityConfiguration page_accessibility) { |
| 117 DCHECK(len >= kPageAllocationGranularity); | 117 DCHECK(length >= kPageAllocationGranularity); |
| 118 DCHECK(!(len & kPageAllocationGranularityOffsetMask)); | 118 DCHECK(!(length & kPageAllocationGranularityOffsetMask)); |
| 119 DCHECK(align >= kPageAllocationGranularity); | 119 DCHECK(align >= kPageAllocationGranularity); |
| 120 DCHECK(!(align & kPageAllocationGranularityOffsetMask)); | 120 DCHECK(!(align & kPageAllocationGranularityOffsetMask)); |
| 121 DCHECK(!(reinterpret_cast<uintptr_t>(addr) & | 121 DCHECK(!(reinterpret_cast<uintptr_t>(address) & |
| 122 kPageAllocationGranularityOffsetMask)); | 122 kPageAllocationGranularityOffsetMask)); |
| 123 uintptr_t alignOffsetMask = align - 1; | 123 uintptr_t align_offset_mask = align - 1; |
| 124 uintptr_t alignBaseMask = ~alignOffsetMask; | 124 uintptr_t align_base_mask = ~align_offset_mask; |
| 125 DCHECK(!(reinterpret_cast<uintptr_t>(addr) & alignOffsetMask)); | 125 DCHECK(!(reinterpret_cast<uintptr_t>(address) & align_offset_mask)); |
| 126 | 126 |
| 127 // If the client passed null as the address, choose a good one. | 127 // If the client passed null as the address, choose a good one. |
| 128 if (!addr) { | 128 if (!address) { |
| 129 addr = getRandomPageBase(); | 129 address = GetRandomPageBase(); |
| 130 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & | 130 address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) & |
| 131 alignBaseMask); | 131 align_base_mask); |
| 132 } | 132 } |
| 133 | 133 |
| 134 // First try to force an exact-size, aligned allocation from our random base. | 134 // First try to force an exact-size, aligned allocation from our random base. |
| 135 for (int count = 0; count < 3; ++count) { | 135 for (int count = 0; count < 3; ++count) { |
| 136 void* ret = systemAllocPages(addr, len, pageAccessibility); | 136 void* ret = SystemAllocPages(address, length, page_accessibility); |
| 137 if (kHintIsAdvisory || ret) { | 137 if (kHintIsAdvisory || ret) { |
| 138 // If the alignment is to our liking, we're done. | 138 // If the alignment is to our liking, we're done. |
| 139 if (!(reinterpret_cast<uintptr_t>(ret) & alignOffsetMask)) | 139 if (!(reinterpret_cast<uintptr_t>(ret) & align_offset_mask)) |
| 140 return ret; | 140 return ret; |
| 141 freePages(ret, len); | 141 FreePages(ret, length); |
| 142 #if defined(ARCH_CPU_32_BITS) | 142 #if defined(ARCH_CPU_32_BITS) |
| 143 addr = reinterpret_cast<void*>( | 143 address = reinterpret_cast<void*>( |
| 144 (reinterpret_cast<uintptr_t>(ret) + align) & alignBaseMask); | 144 (reinterpret_cast<uintptr_t>(ret) + align) & align_base_mask); |
| 145 #endif | 145 #endif |
| 146 } else if (!addr) { // We know we're OOM when an unhinted allocation fails. | 146 } else if (!address) { // We know we're OOM when an unhinted allocation |
| 147 // fails. |
| 147 return nullptr; | 148 return nullptr; |
| 148 | |
| 149 } else { | 149 } else { |
| 150 #if defined(ARCH_CPU_32_BITS) | 150 #if defined(ARCH_CPU_32_BITS) |
| 151 addr = reinterpret_cast<char*>(addr) + align; | 151 address = reinterpret_cast<char*>(address) + align; |
| 152 #endif | 152 #endif |
| 153 } | 153 } |
| 154 | 154 |
| 155 #if !defined(ARCH_CPU_32_BITS) | 155 #if !defined(ARCH_CPU_32_BITS) |
| 156 // Keep trying random addresses on systems that have a large address space. | 156 // Keep trying random addresses on systems that have a large address space. |
| 157 addr = getRandomPageBase(); | 157 address = GetRandomPageBase(); |
| 158 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & | 158 address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) & |
| 159 alignBaseMask); | 159 align_base_mask); |
| 160 #endif | 160 #endif |
| 161 } | 161 } |
| 162 | 162 |
| 163 // Map a larger allocation so we can force alignment, but continue randomizing | 163 // Map a larger allocation so we can force alignment, but continue randomizing |
| 164 // only on 64-bit POSIX. | 164 // only on 64-bit POSIX. |
| 165 size_t tryLen = len + (align - kPageAllocationGranularity); | 165 size_t try_length = length + (align - kPageAllocationGranularity); |
| 166 CHECK(tryLen >= len); | 166 CHECK(try_length >= length); |
| 167 void* ret; | 167 void* ret; |
| 168 | 168 |
| 169 do { | 169 do { |
| 170 // Don't continue to burn cycles on mandatory hints (Windows). | 170 // Don't continue to burn cycles on mandatory hints (Windows). |
| 171 addr = kHintIsAdvisory ? getRandomPageBase() : nullptr; | 171 address = kHintIsAdvisory ? GetRandomPageBase() : nullptr; |
| 172 ret = systemAllocPages(addr, tryLen, pageAccessibility); | 172 ret = SystemAllocPages(address, try_length, page_accessibility); |
| 173 // The retries are for Windows, where a race can steal our mapping on | 173 // The retries are for Windows, where a race can steal our mapping on |
| 174 // resize. | 174 // resize. |
| 175 } while (ret && | 175 } while (ret && |
| 176 (ret = trimMapping(ret, tryLen, len, align, pageAccessibility)) == | 176 (ret = TrimMapping(ret, try_length, length, align, |
| 177 nullptr); | 177 page_accessibility)) == nullptr); |
| 178 | 178 |
| 179 return ret; | 179 return ret; |
| 180 } | 180 } |
| 181 | 181 |
| 182 void freePages(void* addr, size_t len) { | 182 void FreePages(void* address, size_t length) { |
| 183 DCHECK(!(reinterpret_cast<uintptr_t>(addr) & | 183 DCHECK(!(reinterpret_cast<uintptr_t>(address) & |
| 184 kPageAllocationGranularityOffsetMask)); | 184 kPageAllocationGranularityOffsetMask)); |
| 185 DCHECK(!(len & kPageAllocationGranularityOffsetMask)); | 185 DCHECK(!(length & kPageAllocationGranularityOffsetMask)); |
| 186 #if defined(OS_POSIX) | 186 #if defined(OS_POSIX) |
| 187 int ret = munmap(addr, len); | 187 int ret = munmap(address, length); |
| 188 CHECK(!ret); | 188 CHECK(!ret); |
| 189 #else | 189 #else |
| 190 BOOL ret = VirtualFree(addr, 0, MEM_RELEASE); | 190 BOOL ret = VirtualFree(address, 0, MEM_RELEASE); |
| 191 CHECK(ret); | 191 CHECK(ret); |
| 192 #endif | 192 #endif |
| 193 } | 193 } |
| 194 | 194 |
| 195 void setSystemPagesInaccessible(void* addr, size_t len) { | 195 void SetSystemPagesInaccessible(void* address, size_t length) { |
| 196 DCHECK(!(len & kSystemPageOffsetMask)); | 196 DCHECK(!(length & kSystemPageOffsetMask)); |
| 197 #if defined(OS_POSIX) | 197 #if defined(OS_POSIX) |
| 198 int ret = mprotect(addr, len, PROT_NONE); | 198 int ret = mprotect(address, length, PROT_NONE); |
| 199 CHECK(!ret); | 199 CHECK(!ret); |
| 200 #else | 200 #else |
| 201 BOOL ret = VirtualFree(addr, len, MEM_DECOMMIT); | 201 BOOL ret = VirtualFree(address, length, MEM_DECOMMIT); |
| 202 CHECK(ret); | 202 CHECK(ret); |
| 203 #endif | 203 #endif |
| 204 } | 204 } |
| 205 | 205 |
| 206 bool setSystemPagesAccessible(void* addr, size_t len) { | 206 bool SetSystemPagesAccessible(void* address, size_t length) { |
| 207 DCHECK(!(len & kSystemPageOffsetMask)); | 207 DCHECK(!(length & kSystemPageOffsetMask)); |
| 208 #if defined(OS_POSIX) | 208 #if defined(OS_POSIX) |
| 209 return !mprotect(addr, len, PROT_READ | PROT_WRITE); | 209 return !mprotect(address, length, PROT_READ | PROT_WRITE); |
| 210 #else | 210 #else |
| 211 return !!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE); | 211 return !!VirtualAlloc(address, length, MEM_COMMIT, PAGE_READWRITE); |
| 212 #endif | 212 #endif |
| 213 } | 213 } |
| 214 | 214 |
| 215 void decommitSystemPages(void* addr, size_t len) { | 215 void DecommitSystemPages(void* address, size_t length) { |
| 216 DCHECK(!(len & kSystemPageOffsetMask)); | 216 DCHECK(!(length & kSystemPageOffsetMask)); |
| 217 #if defined(OS_POSIX) | 217 #if defined(OS_POSIX) |
| 218 int ret = madvise(addr, len, MADV_FREE); | 218 int ret = madvise(address, length, MADV_FREE); |
| 219 if (ret != 0 && errno == EINVAL) { | 219 if (ret != 0 && errno == EINVAL) { |
| 220 // MADV_FREE only works on Linux 4.5+ . If request failed, | 220 // MADV_FREE only works on Linux 4.5+ . If request failed, |
| 221 // retry with older MADV_DONTNEED . Note that MADV_FREE | 221 // retry with older MADV_DONTNEED . Note that MADV_FREE |
| 222 // being defined at compile time doesn't imply runtime support. | 222 // being defined at compile time doesn't imply runtime support. |
| 223 ret = madvise(addr, len, MADV_DONTNEED); | 223 ret = madvise(address, length, MADV_DONTNEED); |
| 224 } | 224 } |
| 225 CHECK(!ret); | 225 CHECK(!ret); |
| 226 #else | 226 #else |
| 227 setSystemPagesInaccessible(addr, len); | 227 SetSystemPagesInaccessible(address, length); |
| 228 #endif | 228 #endif |
| 229 } | 229 } |
| 230 | 230 |
| 231 void recommitSystemPages(void* addr, size_t len) { | 231 void RecommitSystemPages(void* address, size_t length) { |
| 232 DCHECK(!(len & kSystemPageOffsetMask)); | 232 DCHECK(!(length & kSystemPageOffsetMask)); |
| 233 #if defined(OS_POSIX) | 233 #if defined(OS_POSIX) |
| 234 (void)addr; | 234 (void)address; |
| 235 #else | 235 #else |
| 236 CHECK(setSystemPagesAccessible(addr, len)); | 236 CHECK(SetSystemPagesAccessible(address, length)); |
| 237 #endif | 237 #endif |
| 238 } | 238 } |
| 239 | 239 |
| 240 void discardSystemPages(void* addr, size_t len) { | 240 void DiscardSystemPages(void* address, size_t length) { |
| 241 DCHECK(!(len & kSystemPageOffsetMask)); | 241 DCHECK(!(length & kSystemPageOffsetMask)); |
| 242 #if defined(OS_POSIX) | 242 #if defined(OS_POSIX) |
| 243 // On POSIX, the implementation detail is that discard and decommit are the | 243 // On POSIX, the implementation detail is that discard and decommit are the |
| 244 // same, and lead to pages that are returned to the system immediately and | 244 // same, and lead to pages that are returned to the system immediately and |
| 245 // get replaced with zeroed pages when touched. So we just call | 245 // get replaced with zeroed pages when touched. So we just call |
| 246 // decommitSystemPages() here to avoid code duplication. | 246 // DecommitSystemPages() here to avoid code duplication. |
| 247 decommitSystemPages(addr, len); | 247 DecommitSystemPages(address, length); |
| 248 #else | 248 #else |
| 249 // On Windows discarded pages are not returned to the system immediately and | 249 // On Windows discarded pages are not returned to the system immediately and |
| 250 // not guaranteed to be zeroed when returned to the application. | 250 // not guaranteed to be zeroed when returned to the application. |
| 251 using DiscardVirtualMemoryFunction = | 251 using DiscardVirtualMemoryFunction = |
| 252 DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size); | 252 DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size); |
| 253 static DiscardVirtualMemoryFunction discardVirtualMemory = | 253 static DiscardVirtualMemoryFunction discard_virtual_memory = |
| 254 reinterpret_cast<DiscardVirtualMemoryFunction>(-1); | 254 reinterpret_cast<DiscardVirtualMemoryFunction>(-1); |
| 255 if (discardVirtualMemory == | 255 if (discard_virtual_memory == |
| 256 reinterpret_cast<DiscardVirtualMemoryFunction>(-1)) | 256 reinterpret_cast<DiscardVirtualMemoryFunction>(-1)) |
| 257 discardVirtualMemory = | 257 discard_virtual_memory = |
| 258 reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress( | 258 reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress( |
| 259 GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory")); | 259 GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory")); |
| 260 // Use DiscardVirtualMemory when available because it releases faster than | 260 // Use DiscardVirtualMemory when available because it releases faster than |
| 261 // MEM_RESET. | 261 // MEM_RESET. |
| 262 DWORD ret = 1; | 262 DWORD ret = 1; |
| 263 if (discardVirtualMemory) | 263 if (discard_virtual_memory) |
| 264 ret = discardVirtualMemory(addr, len); | 264 ret = discard_virtual_memory(address, length); |
| 265 // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on | 265 // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on |
| 266 // failure. | 266 // failure. |
| 267 if (ret) { | 267 if (ret) { |
| 268 void* ret = VirtualAlloc(addr, len, MEM_RESET, PAGE_READWRITE); | 268 void* ret = VirtualAlloc(address, length, MEM_RESET, PAGE_READWRITE); |
| 269 CHECK(ret); | 269 CHECK(ret); |
| 270 } | 270 } |
| 271 #endif | 271 #endif |
| 272 } | 272 } |
| 273 | 273 |
| 274 uint32_t getAllocPageErrorCode() { | 274 uint32_t GetAllocPageErrorCode() { |
| 275 return base::subtle::Acquire_Load(&s_allocPageErrorCode); | 275 return base::subtle::Acquire_Load(&s_allocPageErrorCode); |
| 276 } | 276 } |
| 277 | 277 |
| 278 } // namespace base | 278 } // namespace base |
| OLD | NEW |