Poison.cpp (6447B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * A poison value that can be used to fill a memory space with 9 * an address that leads to a safe crash when dereferenced. 10 */ 11 12 #include "mozilla/Poison.h" 13 14 #include "mozilla/Assertions.h" 15 #ifdef _WIN32 16 # include <windows.h> 17 #elif !defined(__OS2__) 18 # include <unistd.h> 19 # ifndef __wasi__ 20 # include <sys/mman.h> 21 # ifndef MAP_ANON 22 # ifdef MAP_ANONYMOUS 23 # define MAP_ANON MAP_ANONYMOUS 24 # else 25 # error "Don't know how to get anonymous memory" 26 # endif 27 # endif 28 # endif 29 #endif 30 31 // Freed memory is filled with a poison value, which we arrange to 32 // form a pointer either to an always-unmapped region of the address 33 // space, or to a page that has been reserved and rendered 34 // inaccessible via OS primitives. See tests/TestPoisonArea.cpp for 35 // extensive discussion of the requirements for this page. The code 36 // from here to 'class FreeList' needs to be kept in sync with that 37 // file. 38 39 #ifdef _WIN32 40 static void* ReserveRegion(uintptr_t aRegion, uintptr_t aSize) { 41 return VirtualAlloc((void*)aRegion, aSize, MEM_RESERVE, PAGE_NOACCESS); 42 } 43 44 static void ReleaseRegion(void* aRegion, uintptr_t aSize) { 45 VirtualFree(aRegion, aSize, MEM_RELEASE); 46 } 47 48 static bool ProbeRegion(uintptr_t aRegion, uintptr_t aSize) { 49 SYSTEM_INFO sinfo; 50 GetSystemInfo(&sinfo); 51 if (aRegion >= (uintptr_t)sinfo.lpMaximumApplicationAddress && 52 aRegion + aSize >= (uintptr_t)sinfo.lpMaximumApplicationAddress) { 53 return true; 54 } else { 55 return false; 56 } 57 } 58 59 static uintptr_t GetDesiredRegionSize() { 60 SYSTEM_INFO sinfo; 61 GetSystemInfo(&sinfo); 62 return sinfo.dwAllocationGranularity; 63 } 64 65 # define RESERVE_FAILED 0 66 67 #elif defined(__OS2__) 68 static void* ReserveRegion(uintptr_t aRegion, uintptr_t aSize) { 69 // OS/2 doesn't support allocation at an arbitrary address, 70 // so return an address that is known to be invalid. 71 return (void*)0xFFFD0000; 72 } 73 74 static void ReleaseRegion(void* aRegion, uintptr_t aSize) { return; } 75 76 static bool ProbeRegion(uintptr_t aRegion, uintptr_t aSize) { 77 // There's no reliable way to probe an address in the system 78 // arena other than by touching it and seeing if a trap occurs. 79 return false; 80 } 81 82 static uintptr_t GetDesiredRegionSize() { 83 // Page size is fixed at 4k. 84 return 0x1000; 85 } 86 87 # define RESERVE_FAILED 0 88 89 #elif defined(__wasi__) 90 91 # define RESERVE_FAILED 0 92 93 static void* ReserveRegion(uintptr_t aRegion, uintptr_t aSize) { 94 return RESERVE_FAILED; 95 } 96 97 static void ReleaseRegion(void* aRegion, uintptr_t aSize) { return; } 98 99 static bool ProbeRegion(uintptr_t aRegion, uintptr_t aSize) { 100 const auto pageSize = 1 << 16; 101 MOZ_ASSERT(pageSize == sysconf(_SC_PAGESIZE)); 102 auto heapSize = __builtin_wasm_memory_size(0) * pageSize; 103 return aRegion + aSize < heapSize; 104 } 105 106 static uintptr_t GetDesiredRegionSize() { return 0; } 107 108 #else // __wasi__ 109 110 # include "mozilla/TaggedAnonymousMemory.h" 111 112 static void* ReserveRegion(uintptr_t aRegion, uintptr_t aSize) { 113 return MozTaggedAnonymousMmap(reinterpret_cast<void*>(aRegion), aSize, 114 PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0, 115 "poison"); 116 } 117 118 static void ReleaseRegion(void* aRegion, uintptr_t aSize) { 119 munmap(aRegion, aSize); 120 } 121 122 static bool ProbeRegion(uintptr_t aRegion, uintptr_t aSize) { 123 # ifdef XP_SOLARIS 124 if (posix_madvise(reinterpret_cast<void*>(aRegion), aSize, 125 POSIX_MADV_NORMAL)) { 126 # else 127 if (madvise(reinterpret_cast<void*>(aRegion), aSize, MADV_NORMAL)) { 128 # endif 129 return true; 130 } 131 return false; 132 } 133 134 static uintptr_t GetDesiredRegionSize() { return sysconf(_SC_PAGESIZE); } 135 136 # define RESERVE_FAILED MAP_FAILED 137 138 #endif // system dependencies 139 140 static_assert((sizeof(uintptr_t) == 4 || sizeof(uintptr_t) == 8) && 141 (sizeof(uintptr_t) == sizeof(void*))); 142 143 static uintptr_t ReservePoisonArea(uintptr_t rgnsize) { 144 if (sizeof(uintptr_t) == 8) { 145 // Use the hardware-inaccessible region. 146 // We have to avoid 64-bit constants and shifts by 32 bits, since this 147 // code is compiled in 32-bit mode, although it is never executed there. 148 return (((uintptr_t(0x7FFFFFFFu) << 31) << 1 | uintptr_t(0xF0DEAFFFu)) & 149 ~(rgnsize - 1)); 150 } 151 152 // First see if we can allocate the preferred poison address from the OS. 153 uintptr_t candidate = (0xF0DEAFFF & ~(rgnsize - 1)); 154 void* result = ReserveRegion(candidate, rgnsize); 155 if (result == (void*)candidate) { 156 // success - inaccessible page allocated 157 return candidate; 158 } 159 160 // That didn't work, so see if the preferred address is within a range 161 // of permanently inacessible memory. 162 if (ProbeRegion(candidate, rgnsize)) { 163 // success - selected page cannot be usable memory 164 if (result != RESERVE_FAILED) { 165 ReleaseRegion(result, rgnsize); 166 } 167 return candidate; 168 } 169 170 // The preferred address is already in use. Did the OS give us a 171 // consolation prize? 172 if (result != RESERVE_FAILED) { 173 return uintptr_t(result); 174 } 175 176 // It didn't, so try to allocate again, without any constraint on 177 // the address. 178 result = ReserveRegion(0, rgnsize); 179 if (result != RESERVE_FAILED) { 180 return uintptr_t(result); 181 } 182 183 MOZ_CRASH("no usable poison region identified"); 184 } 185 186 static uintptr_t GetPoisonValue(uintptr_t aBase, uintptr_t aSize) { 187 if (aSize == 0) { // can't happen 188 return 0; 189 } 190 return aBase + aSize / 2 - 1; 191 } 192 193 // Poison is used so pervasively throughout the codebase that we decided it was 194 // best to actually use ordered dynamic initialization of globals (AKA static 195 // constructors) for this. This way everything will have properly initialized 196 // poison -- except other dynamic initialization code in libmozglue, which there 197 // shouldn't be much of. (libmozglue is one of the first things loaded, and 198 // specifically comes before libxul, so nearly all gecko code runs strictly 199 // after this.) 200 extern "C" { 201 MOZ_RUNINIT uintptr_t gMozillaPoisonSize = GetDesiredRegionSize(); 202 MOZ_RUNINIT uintptr_t gMozillaPoisonBase = 203 ReservePoisonArea(gMozillaPoisonSize); 204 MOZ_RUNINIT uintptr_t gMozillaPoisonValue = 205 GetPoisonValue(gMozillaPoisonBase, gMozillaPoisonSize); 206 }