Line data Source code
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 0 : ReserveRegion(uintptr_t aRegion, uintptr_t aSize)
117 : {
118 0 : return MozTaggedAnonymousMmap(reinterpret_cast<void*>(aRegion), aSize,
119 : PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0,
120 0 : "poison");
121 : }
122 :
123 : static void
124 0 : ReleaseRegion(void* aRegion, uintptr_t aSize)
125 : {
126 0 : munmap(aRegion, aSize);
127 0 : }
128 :
129 : static bool
130 0 : ProbeRegion(uintptr_t aRegion, uintptr_t aSize)
131 : {
132 : #ifdef XP_SOLARIS
133 : if (posix_madvise(reinterpret_cast<void*>(aRegion), aSize, POSIX_MADV_NORMAL)) {
134 : #else
135 0 : if (madvise(reinterpret_cast<void*>(aRegion), aSize, MADV_NORMAL)) {
136 : #endif
137 0 : return true;
138 : } else {
139 0 : return false;
140 : }
141 : }
142 :
143 : static uintptr_t
144 3 : GetDesiredRegionSize()
145 : {
146 3 : return sysconf(_SC_PAGESIZE);
147 : }
148 :
149 : #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 3 : ReservePoisonArea(uintptr_t rgnsize)
158 : {
159 : if (sizeof(uintptr_t) == 8) {
160 : // Use the hardware-inaccessible region.
161 : // We have to avoid 64-bit constants and shifts by 32 bits, since this
162 : // code is compiled in 32-bit mode, although it is never executed there.
163 : return
164 : (((uintptr_t(0x7FFFFFFFu) << 31) << 1 | uintptr_t(0xF0DEAFFFu))
165 3 : & ~(rgnsize-1));
166 : }
167 :
168 : // First see if we can allocate the preferred poison address from the OS.
169 : uintptr_t candidate = (0xF0DEAFFF & ~(rgnsize-1));
170 : void* result = ReserveRegion(candidate, rgnsize);
171 : if (result == (void*)candidate) {
172 : // success - inaccessible page allocated
173 : return candidate;
174 : }
175 :
176 : // That didn't work, so see if the preferred address is within a range
177 : // of permanently inacessible memory.
178 : if (ProbeRegion(candidate, rgnsize)) {
179 : // success - selected page cannot be usable memory
180 : if (result != RESERVE_FAILED) {
181 : ReleaseRegion(result, rgnsize);
182 : }
183 : return candidate;
184 : }
185 :
186 : // The preferred address is already in use. Did the OS give us a
187 : // consolation prize?
188 : if (result != RESERVE_FAILED) {
189 : return uintptr_t(result);
190 : }
191 :
192 : // It didn't, so try to allocate again, without any constraint on
193 : // the address.
194 : result = ReserveRegion(0, rgnsize);
195 : if (result != RESERVE_FAILED) {
196 : return uintptr_t(result);
197 : }
198 :
199 : MOZ_CRASH("no usable poison region identified");
200 : }
201 :
202 : void
203 3 : mozPoisonValueInit()
204 : {
205 3 : gMozillaPoisonSize = GetDesiredRegionSize();
206 3 : gMozillaPoisonBase = ReservePoisonArea(gMozillaPoisonSize);
207 :
208 3 : if (gMozillaPoisonSize == 0) { // can't happen
209 0 : return;
210 : }
211 3 : gMozillaPoisonValue = gMozillaPoisonBase + gMozillaPoisonSize / 2 - 1;
212 : }
|