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