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/.
8 /* Code in this file needs to be kept in sync with code in nsPresArena.cpp.
10 * We want to use a fixed address for frame poisoning so that it is readily
11 * identifiable in crash dumps. Whether such an address is available
12 * without any special setup depends on the system configuration.
14 * All current 64-bit CPUs (with the possible exception of PowerPC64)
15 * reserve the vast majority of the virtual address space for future
16 * hardware extensions; valid addresses must be below some break point
17 * between 2**48 and 2**54, depending on exactly which chip you have. Some
18 * chips (notably amd64) also allow the use of the *highest* 2**48 -- 2**54
19 * addresses. Thus, if user space pointers are 64 bits wide, we can just
20 * use an address outside this range, and no more is required. To
21 * accommodate the chips that allow very high addresses to be valid, the
22 * value chosen is close to 2**63 (that is, in the middle of the space).
24 * In most cases, a purely 32-bit operating system must reserve some
25 * fraction of the address space for its own use. Contemporary 32-bit OSes
26 * tend to take the high gigabyte or so (0xC000_0000 on up). If we can
27 * prove that high addresses are reserved to the kernel, we can use an
28 * address in that region. Unfortunately, not all 32-bit OSes do this;
29 * OSX 10.4 might not, and it is unclear what mobile OSes are like
30 * (some 32-bit CPUs make it very easy for the kernel to exist in its own
31 * private address space).
33 * Furthermore, when a 32-bit user space process is running on a 64-bit
34 * kernel, the operating system has no need to reserve any of the space that
35 * the process can see, and generally does not do so. This is the scenario
36 * of greatest concern, since it covers all contemporary OSX iterations
37 * (10.5+) as well as Windows Vista and 7 on newer amd64 hardware. Linux on
38 * amd64 is generally run as a pure 64-bit environment, but its 32-bit
39 * compatibility mode also has this property.
41 * Thus, when user space pointers are 32 bits wide, we need to validate
42 * our chosen address, and possibly *make* it a good poison address by
43 * allocating a page around it and marking it inaccessible. The algorithm
46 * 1. Attempt to make the page surrounding the poison address a reserved,
47 * inaccessible memory region using OS primitives. On Windows, this is
48 * done with VirtualAlloc(MEM_RESERVE); on Unix, mmap(PROT_NONE).
50 * 2. If mmap/VirtualAlloc failed, there are two possible reasons: either
51 * the region is reserved to the kernel and no further action is
52 * required, or there is already usable memory in this area and we have
53 * to pick a different address. The tricky part is knowing which case
54 * we have, without attempting to access the region. On Windows, we
55 * rely on GetSystemInfo()'s reported upper and lower bounds of the
56 * application memory area. On Unix, there is nothing devoted to the
57 * purpose, but seeing if madvise() fails is close enough (it *might*
58 * disrupt someone else's use of the memory region, but not by as much
59 * as anything else available).
61 * Be aware of these gotchas:
63 * 1. We cannot use mmap() with MAP_FIXED. MAP_FIXED is defined to
64 * _replace_ any existing mapping in the region, if necessary to satisfy
65 * the request. Obviously, as we are blindly attempting to acquire a
66 * page at a constant address, we must not do this, lest we overwrite
67 * someone else's allocation.
69 * 2. For the same reason, we cannot blindly use mprotect() if mmap() fails.
71 * 3. madvise() may fail when applied to a 'magic' memory region provided as
72 * a kernel/user interface. Fortunately, the only such case I know about
73 * is the "vsyscall" area (not to be confused with the "vdso" area) for
74 * *64*-bit processes on Linux - and we don't even run this code for
77 * 4. VirtualQuery() does not produce any useful information if
78 * applied to kernel memory - in fact, it doesn't write its output
79 * at all. Thus, it is not used here.
82 #include "mozilla/IntegerPrintfMacros.h"
83 #include "mozilla/NullPtr.h"
85 // MAP_ANON(YMOUS) is not in any standard. Add defines as necessary.
87 #define _DARWIN_C_SOURCE 1
99 #include <sys/types.h>
103 #include <sys/stat.h>
104 #include <sys/wait.h>
106 #include <sys/mman.h>
109 #define MAP_ANON MAP_ANONYMOUS
111 #error "Don't know how to get anonymous memory"
116 #define SIZxPTR ((int)(sizeof(uintptr_t)*2))
118 /* This program assumes that a whole number of return instructions fit into
119 * 32 bits, and that 32-bit alignment is sufficient for a branch destination.
120 * For architectures where this is not true, fiddling with RETURN_INSTR_TYPE
124 #if defined __i386__ || defined __x86_64__ || \
125 defined __i386 || defined __x86_64 || \
126 defined _M_IX86 || defined _M_AMD64
127 #define RETURN_INSTR 0xC3C3C3C3 /* ret; ret; ret; ret */
129 #elif defined __arm__ || defined _M_ARM
130 #define RETURN_INSTR 0xE12FFF1E /* bx lr */
132 // PPC has its own style of CPU-id #defines. There is no Windows for
133 // PPC as far as I know, so no _M_ variant.
134 #elif defined _ARCH_PPC || defined _ARCH_PWR || defined _ARCH_PWR2
135 #define RETURN_INSTR 0x4E800020 /* blr */
137 #elif defined __sparc || defined __sparcv9
138 #define RETURN_INSTR 0x81c3e008 /* retl */
140 #elif defined __alpha
141 #define RETURN_INSTR 0x6bfa8001 /* ret */
144 #define RETURN_INSTR 0xe840c002 /* bv,n r0(rp) */
147 #define RETURN_INSTR 0x03e00008 /* jr ra */
150 /* On mipsel, jr ra needs to be followed by a nop.
151 0x03e00008 as a 64 bits integer just does that */
152 #define RETURN_INSTR_TYPE uint64_t
155 #elif defined __s390__
156 #define RETURN_INSTR 0x07fe0000 /* br %r14 */
158 #elif defined __aarch64__
159 #define RETURN_INSTR 0xd65f03c0 /* ret */
162 struct ia64_instr
{ uint32_t mI
[4]; };
163 static const ia64_instr _return_instr
=
164 {{ 0x00000011, 0x00000001, 0x80000200, 0x00840008 }}; /* br.ret.sptk.many b0 */
166 #define RETURN_INSTR _return_instr
167 #define RETURN_INSTR_TYPE ia64_instr
170 #error "Need return instruction for this architecture"
173 #ifndef RETURN_INSTR_TYPE
174 #define RETURN_INSTR_TYPE uint32_t
177 // Miscellaneous Windows/Unix portability gumph
180 // Uses of this function deliberately leak the string.
182 StrW32Error(DWORD aErrcode
)
185 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
186 FORMAT_MESSAGE_FROM_SYSTEM
|
187 FORMAT_MESSAGE_IGNORE_INSERTS
,
188 nullptr, aErrcode
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
189 (LPSTR
)&errmsg
, 0, nullptr);
191 // FormatMessage puts an unwanted newline at the end of the string
192 size_t n
= strlen(errmsg
)-1;
193 while (errmsg
[n
] == '\r' || errmsg
[n
] == '\n') {
199 #define LastErrMsg() (StrW32Error(GetLastError()))
201 // Because we use VirtualAlloc in MEM_RESERVE mode, the "page size" we want
202 // is the allocation granularity.
203 static SYSTEM_INFO sInfo_
;
205 static inline uint32_t
208 return sInfo_
.dwAllocationGranularity
;
212 ReserveRegion(uintptr_t aRequest
, bool aAccessible
)
214 return VirtualAlloc((void*)aRequest
, PageSize(),
215 aAccessible
? MEM_RESERVE
|MEM_COMMIT
: MEM_RESERVE
,
216 aAccessible
? PAGE_EXECUTE_READWRITE
: PAGE_NOACCESS
);
220 ReleaseRegion(void* aPage
)
222 VirtualFree(aPage
, PageSize(), MEM_RELEASE
);
226 ProbeRegion(uintptr_t aPage
)
228 return aPage
>= (uintptr_t)sInfo_
.lpMaximumApplicationAddress
&&
229 aPage
+ PageSize() >= (uintptr_t)sInfo_
.lpMaximumApplicationAddress
;
233 MakeRegionExecutable(void*)
243 #define LastErrMsg() (strerror(errno))
245 static unsigned long gUnixPageSize
;
247 static inline unsigned long
250 return gUnixPageSize
;
254 ReserveRegion(uintptr_t aRequest
, bool aAccessible
)
256 return mmap(reinterpret_cast<void*>(aRequest
), PageSize(),
257 aAccessible
? PROT_READ
|PROT_WRITE
: PROT_NONE
,
258 MAP_PRIVATE
|MAP_ANON
, -1, 0);
262 ReleaseRegion(void* aPage
)
264 munmap(aPage
, PageSize());
268 ProbeRegion(uintptr_t aPage
)
270 return !!madvise(reinterpret_cast<void*>(aPage
), PageSize(), MADV_NORMAL
);
274 MakeRegionExecutable(void* aPage
)
276 return mprotect((caddr_t
)aPage
, PageSize(), PROT_READ
|PROT_WRITE
|PROT_EXEC
);
284 if (sizeof(uintptr_t) == 8) {
285 // Use the hardware-inaccessible region.
286 // We have to avoid 64-bit constants and shifts by 32 bits, since this
287 // code is compiled in 32-bit mode, although it is never executed there.
288 uintptr_t result
= (((uintptr_t(0x7FFFFFFFu
) << 31) << 1 |
289 uintptr_t(0xF0DEAFFFu
)) &
290 ~uintptr_t(PageSize()-1));
291 printf("INFO | poison area assumed at 0x%.*" PRIxPTR
"\n", SIZxPTR
, result
);
295 // First see if we can allocate the preferred poison address from the OS.
296 uintptr_t candidate
= (0xF0DEAFFF & ~(PageSize() - 1));
297 void* result
= ReserveRegion(candidate
, false);
298 if (result
== reinterpret_cast<void*>(candidate
)) {
299 // success - inaccessible page allocated
300 printf("INFO | poison area allocated at 0x%.*" PRIxPTR
301 " (preferred addr)\n", SIZxPTR
, reinterpret_cast<uintptr_t>(result
));
305 // That didn't work, so see if the preferred address is within a range
306 // of permanently inacessible memory.
307 if (ProbeRegion(candidate
)) {
308 // success - selected page cannot be usable memory
309 if (result
!= MAP_FAILED
) {
310 ReleaseRegion(result
);
312 printf("INFO | poison area assumed at 0x%.*" PRIxPTR
313 " (preferred addr)\n", SIZxPTR
, candidate
);
317 // The preferred address is already in use. Did the OS give us a
318 // consolation prize?
319 if (result
!= MAP_FAILED
) {
320 uintptr_t ures
= reinterpret_cast<uintptr_t>(result
);
321 printf("INFO | poison area allocated at 0x%.*" PRIxPTR
322 " (consolation prize)\n", SIZxPTR
, ures
);
326 // It didn't, so try to allocate again, without any constraint on
328 result
= ReserveRegion(0, false);
329 if (result
!= MAP_FAILED
) {
330 uintptr_t ures
= reinterpret_cast<uintptr_t>(result
);
331 printf("INFO | poison area allocated at 0x%.*" PRIxPTR
332 " (fallback)\n", SIZxPTR
, ures
);
336 printf("ERROR | no usable poison area found\n");
340 /* The "positive control" area confirms that we can allocate a page with the
341 * proper characteristics.
344 ReservePositiveControl()
347 void* result
= ReserveRegion(0, false);
348 if (result
== MAP_FAILED
) {
349 printf("ERROR | allocating positive control | %s\n", LastErrMsg());
352 printf("INFO | positive control allocated at 0x%.*" PRIxPTR
"\n",
353 SIZxPTR
, (uintptr_t)result
);
354 return (uintptr_t)result
;
357 /* The "negative control" area confirms that our probe logic does detect a
358 * page that is readable, writable, or executable.
361 ReserveNegativeControl()
363 void* result
= ReserveRegion(0, true);
364 if (result
== MAP_FAILED
) {
365 printf("ERROR | allocating negative control | %s\n", LastErrMsg());
369 // Fill the page with return instructions.
370 RETURN_INSTR_TYPE
* p
= reinterpret_cast<RETURN_INSTR_TYPE
*>(result
);
371 RETURN_INSTR_TYPE
* limit
=
372 reinterpret_cast<RETURN_INSTR_TYPE
*>(
373 reinterpret_cast<char*>(result
) + PageSize());
378 // Now mark it executable as well as readable and writable.
379 // (mmap(PROT_EXEC) may fail when applied to anonymous memory.)
381 if (MakeRegionExecutable(result
)) {
382 printf("ERROR | making negative control executable | %s\n", LastErrMsg());
386 printf("INFO | negative control allocated at 0x%.*" PRIxPTR
"\n",
387 SIZxPTR
, (uintptr_t)result
);
388 return (uintptr_t)result
;
392 JumpTo(uintptr_t aOpaddr
)
399 } call
= { aOpaddr
, };
400 ((void (*)())&call
)();
402 ((void (*)())aOpaddr
)();
408 IsBadExecPtr(uintptr_t aPtr
)
412 #if defined(_MSC_VER) && !defined(__clang__)
415 } __except (EXCEPTION_EXECUTE_HANDLER
) {
419 printf("INFO | exec test not supported on MinGW or clang-cl builds\n");
421 ret
= IsBadReadPtr((const void*)aPtr
, 1);
427 /* Test each page. */
429 TestPage(const char* aPageLabel
, uintptr_t aPageAddr
, int aShouldSucceed
)
435 for (unsigned int test
= 0; test
< 3; test
++) {
437 // The execute test must be done before the write test, because the
438 // write test will clobber memory at the target address.
439 case 0: oplabel
= "reading"; opaddr
= aPageAddr
+ PageSize()/2 - 1; break;
440 case 1: oplabel
= "executing"; opaddr
= aPageAddr
+ PageSize()/2; break;
441 case 2: oplabel
= "writing"; opaddr
= aPageAddr
+ PageSize()/2 - 1; break;
449 case 0: badptr
= IsBadReadPtr((const void*)opaddr
, 1); break;
450 case 1: badptr
= IsBadExecPtr(opaddr
); break;
451 case 2: badptr
= IsBadWritePtr((void*)opaddr
, 1); break;
456 if (aShouldSucceed
) {
457 printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel
, aPageLabel
);
460 printf("TEST-PASS | %s %s\n", oplabel
, aPageLabel
);
463 // if control reaches this point the probe succeeded
464 if (aShouldSucceed
) {
465 printf("TEST-PASS | %s %s\n", oplabel
, aPageLabel
);
467 printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel
, aPageLabel
);
474 printf("ERROR | %s %s | fork=%s\n", oplabel
, aPageLabel
,
477 } else if (pid
== 0) {
478 volatile unsigned char scratch
;
480 case 0: scratch
= *(volatile unsigned char*)opaddr
; break;
481 case 1: JumpTo(opaddr
); break;
482 case 2: *(volatile unsigned char*)opaddr
= 0; break;
489 if (waitpid(pid
, &status
, 0) != pid
) {
490 printf("ERROR | %s %s | wait=%s\n", oplabel
, aPageLabel
,
495 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0) {
496 if (aShouldSucceed
) {
497 printf("TEST-PASS | %s %s\n", oplabel
, aPageLabel
);
499 printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected successful exit\n",
500 oplabel
, aPageLabel
);
503 } else if (WIFEXITED(status
)) {
504 printf("ERROR | %s %s | unexpected exit code %d\n",
505 oplabel
, aPageLabel
, WEXITSTATUS(status
));
507 } else if (WIFSIGNALED(status
)) {
508 if (aShouldSucceed
) {
509 printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected signal %d\n",
510 oplabel
, aPageLabel
, WTERMSIG(status
));
513 printf("TEST-PASS | %s %s | signal %d (as expected)\n",
514 oplabel
, aPageLabel
, WTERMSIG(status
));
517 printf("ERROR | %s %s | unexpected exit status %d\n",
518 oplabel
, aPageLabel
, status
);
531 GetSystemInfo(&sInfo_
);
533 gUnixPageSize
= sysconf(_SC_PAGESIZE
);
536 uintptr_t ncontrol
= ReserveNegativeControl();
537 uintptr_t pcontrol
= ReservePositiveControl();
538 uintptr_t poison
= ReservePoisonArea();
540 if (!ncontrol
|| !pcontrol
|| !poison
) {
545 failed
|= TestPage("negative control", ncontrol
, 1);
546 failed
|= TestPage("positive control", pcontrol
, 0);
547 failed
|= TestPage("poison area", poison
, 0);
549 return failed
? 1 : 0;