/src/mozilla-central/mfbt/Poison.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | # include <sys/mman.h> |
20 | | # ifndef MAP_ANON |
21 | | # ifdef MAP_ANONYMOUS |
22 | | # define MAP_ANON MAP_ANONYMOUS |
23 | | # else |
24 | | # error "Don't know how to get anonymous memory" |
25 | | # endif |
26 | | # endif |
27 | | #endif |
28 | | |
29 | | extern "C" { |
30 | | uintptr_t gMozillaPoisonValue; |
31 | | uintptr_t gMozillaPoisonBase; |
32 | | uintptr_t gMozillaPoisonSize; |
33 | | } |
34 | | |
35 | | // Freed memory is filled with a poison value, which we arrange to |
36 | | // form a pointer either to an always-unmapped region of the address |
37 | | // space, or to a page that has been reserved and rendered |
38 | | // inaccessible via OS primitives. See tests/TestPoisonArea.cpp for |
39 | | // extensive discussion of the requirements for this page. The code |
40 | | // from here to 'class FreeList' needs to be kept in sync with that |
41 | | // file. |
42 | | |
43 | | #ifdef _WIN32 |
44 | | static void* |
45 | | ReserveRegion(uintptr_t aRegion, uintptr_t aSize) |
46 | | { |
47 | | return VirtualAlloc((void*)aRegion, aSize, MEM_RESERVE, PAGE_NOACCESS); |
48 | | } |
49 | | |
50 | | static void |
51 | | ReleaseRegion(void* aRegion, uintptr_t aSize) |
52 | | { |
53 | | VirtualFree(aRegion, aSize, MEM_RELEASE); |
54 | | } |
55 | | |
56 | | static bool |
57 | | ProbeRegion(uintptr_t aRegion, uintptr_t aSize) |
58 | | { |
59 | | SYSTEM_INFO sinfo; |
60 | | GetSystemInfo(&sinfo); |
61 | | if (aRegion >= (uintptr_t)sinfo.lpMaximumApplicationAddress && |
62 | | aRegion + aSize >= (uintptr_t)sinfo.lpMaximumApplicationAddress) { |
63 | | return true; |
64 | | } else { |
65 | | return false; |
66 | | } |
67 | | } |
68 | | |
69 | | static uintptr_t |
70 | | GetDesiredRegionSize() |
71 | | { |
72 | | SYSTEM_INFO sinfo; |
73 | | GetSystemInfo(&sinfo); |
74 | | return sinfo.dwAllocationGranularity; |
75 | | } |
76 | | |
77 | | #define RESERVE_FAILED 0 |
78 | | |
79 | | #elif defined(__OS2__) |
80 | | static void* |
81 | | ReserveRegion(uintptr_t aRegion, uintptr_t aSize) |
82 | | { |
83 | | // OS/2 doesn't support allocation at an arbitrary address, |
84 | | // so return an address that is known to be invalid. |
85 | | return (void*)0xFFFD0000; |
86 | | } |
87 | | |
88 | | static void |
89 | | ReleaseRegion(void* aRegion, uintptr_t aSize) |
90 | | { |
91 | | return; |
92 | | } |
93 | | |
94 | | static bool |
95 | | ProbeRegion(uintptr_t aRegion, uintptr_t aSize) |
96 | | { |
97 | | // There's no reliable way to probe an address in the system |
98 | | // arena other than by touching it and seeing if a trap occurs. |
99 | | return false; |
100 | | } |
101 | | |
102 | | static uintptr_t |
103 | | GetDesiredRegionSize() |
104 | | { |
105 | | // Page size is fixed at 4k. |
106 | | return 0x1000; |
107 | | } |
108 | | |
109 | | #define RESERVE_FAILED 0 |
110 | | |
111 | | #else // Unix |
112 | | |
113 | | #include "mozilla/TaggedAnonymousMemory.h" |
114 | | |
115 | | static void* |
116 | | ReserveRegion(uintptr_t aRegion, uintptr_t aSize) |
117 | 0 | { |
118 | 0 | return MozTaggedAnonymousMmap(reinterpret_cast<void*>(aRegion), aSize, |
119 | 0 | PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0, |
120 | 0 | "poison"); |
121 | 0 | } |
122 | | |
123 | | static void |
124 | | ReleaseRegion(void* aRegion, uintptr_t aSize) |
125 | 0 | { |
126 | 0 | munmap(aRegion, aSize); |
127 | 0 | } |
128 | | |
129 | | static bool |
130 | | ProbeRegion(uintptr_t aRegion, uintptr_t aSize) |
131 | 0 | { |
132 | 0 | #ifdef XP_SOLARIS |
133 | 0 | if (posix_madvise(reinterpret_cast<void*>(aRegion), aSize, POSIX_MADV_NORMAL)) { |
134 | 0 | #else |
135 | 0 | if (madvise(reinterpret_cast<void*>(aRegion), aSize, MADV_NORMAL)) { |
136 | 0 | #endif |
137 | 0 | return true; |
138 | 0 | } else { |
139 | 0 | return false; |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | | static uintptr_t |
144 | | GetDesiredRegionSize() |
145 | 3 | { |
146 | 3 | return sysconf(_SC_PAGESIZE); |
147 | 3 | } |
148 | | |
149 | 0 | #define RESERVE_FAILED MAP_FAILED |
150 | | |
151 | | #endif // system dependencies |
152 | | |
153 | | static_assert(sizeof(uintptr_t) == 4 || sizeof(uintptr_t) == 8, ""); |
154 | | static_assert(sizeof(uintptr_t) == sizeof(void*), ""); |
155 | | |
156 | | static uintptr_t |
157 | | ReservePoisonArea(uintptr_t rgnsize) |
158 | 3 | { |
159 | 3 | if (sizeof(uintptr_t) == 8) { |
160 | 3 | // Use the hardware-inaccessible region. |
161 | 3 | // We have to avoid 64-bit constants and shifts by 32 bits, since this |
162 | 3 | // code is compiled in 32-bit mode, although it is never executed there. |
163 | 3 | return |
164 | 3 | (((uintptr_t(0x7FFFFFFFu) << 31) << 1 | uintptr_t(0xF0DEAFFFu)) |
165 | 3 | & ~(rgnsize-1)); |
166 | 3 | } |
167 | 0 | |
168 | 0 | // First see if we can allocate the preferred poison address from the OS. |
169 | 0 | uintptr_t candidate = (0xF0DEAFFF & ~(rgnsize-1)); |
170 | 0 | void* result = ReserveRegion(candidate, rgnsize); |
171 | 0 | if (result == (void*)candidate) { |
172 | 0 | // success - inaccessible page allocated |
173 | 0 | return candidate; |
174 | 0 | } |
175 | 0 | |
176 | 0 | // That didn't work, so see if the preferred address is within a range |
177 | 0 | // of permanently inacessible memory. |
178 | 0 | if (ProbeRegion(candidate, rgnsize)) { |
179 | 0 | // success - selected page cannot be usable memory |
180 | 0 | if (result != RESERVE_FAILED) { |
181 | 0 | ReleaseRegion(result, rgnsize); |
182 | 0 | } |
183 | 0 | return candidate; |
184 | 0 | } |
185 | 0 |
|
186 | 0 | // The preferred address is already in use. Did the OS give us a |
187 | 0 | // consolation prize? |
188 | 0 | if (result != RESERVE_FAILED) { |
189 | 0 | return uintptr_t(result); |
190 | 0 | } |
191 | 0 | |
192 | 0 | // It didn't, so try to allocate again, without any constraint on |
193 | 0 | // the address. |
194 | 0 | result = ReserveRegion(0, rgnsize); |
195 | 0 | if (result != RESERVE_FAILED) { |
196 | 0 | return uintptr_t(result); |
197 | 0 | } |
198 | 0 | |
199 | 0 | MOZ_CRASH("no usable poison region identified"); |
200 | 0 | } |
201 | | |
202 | | void |
203 | | mozPoisonValueInit() |
204 | 3 | { |
205 | 3 | gMozillaPoisonSize = GetDesiredRegionSize(); |
206 | 3 | gMozillaPoisonBase = ReservePoisonArea(gMozillaPoisonSize); |
207 | 3 | |
208 | 3 | if (gMozillaPoisonSize == 0) { // can't happen |
209 | 0 | return; |
210 | 0 | } |
211 | 3 | gMozillaPoisonValue = gMozillaPoisonBase + gMozillaPoisonSize / 2 - 1; |
212 | 3 | } |