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 /* Code in this file needs to be kept in sync with code in nsPresArena.cpp.
9  *
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.
13  *
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).
23  *
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).
32  *
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.
40  *
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
44  * for this is:
45  *
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).
49  *
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).
60  *
61  * Be aware of these gotchas:
62  *
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.
68  *
69  * 2. For the same reason, we cannot blindly use mprotect() if mmap() fails.
70  *
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
75  *    64-bit processes.
76  *
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.
80  */
81 
82 #include "mozilla/IntegerPrintfMacros.h"
83 
84 // MAP_ANON(YMOUS) is not in any standard.  Add defines as necessary.
85 #define _GNU_SOURCE 1
86 #define _DARWIN_C_SOURCE 1
87 
88 #include <stddef.h>
89 
90 #include <errno.h>
91 #include <stdio.h>
92 #include <stdlib.h>
93 #include <string.h>
94 
95 #ifdef _WIN32
96 #include <windows.h>
97 #else
98 #include <sys/types.h>
99 #include <fcntl.h>
100 #include <signal.h>
101 #include <unistd.h>
102 #include <sys/stat.h>
103 #include <sys/wait.h>
104 
105 #include <sys/mman.h>
106 #ifndef MAP_ANON
107 #ifdef MAP_ANONYMOUS
108 #define MAP_ANON MAP_ANONYMOUS
109 #else
110 #error "Don't know how to get anonymous memory"
111 #endif
112 #endif
113 #endif
114 
115 #define SIZxPTR ((int)(sizeof(uintptr_t)*2))
116 
117 /* This program assumes that a whole number of return instructions fit into
118  * 32 bits, and that 32-bit alignment is sufficient for a branch destination.
119  * For architectures where this is not true, fiddling with RETURN_INSTR_TYPE
120  * can be enough.
121  */
122 
123 #if defined __i386__ || defined __x86_64__ ||   \
124   defined __i386 || defined __x86_64 ||         \
125   defined _M_IX86 || defined _M_AMD64
126 #define RETURN_INSTR 0xC3C3C3C3  /* ret; ret; ret; ret */
127 
128 #elif defined __arm__ || defined _M_ARM
129 #define RETURN_INSTR 0xE12FFF1E /* bx lr */
130 
131 // PPC has its own style of CPU-id #defines.  There is no Windows for
132 // PPC as far as I know, so no _M_ variant.
133 #elif defined _ARCH_PPC || defined _ARCH_PWR || defined _ARCH_PWR2
134 #define RETURN_INSTR 0x4E800020 /* blr */
135 
136 #elif defined __sparc || defined __sparcv9
137 #define RETURN_INSTR 0x81c3e008 /* retl */
138 
139 #elif defined __alpha
140 #define RETURN_INSTR 0x6bfa8001 /* ret */
141 
142 #elif defined __hppa
143 #define RETURN_INSTR 0xe840c002 /* bv,n r0(rp) */
144 
145 #elif defined __mips
146 #define RETURN_INSTR 0x03e00008 /* jr ra */
147 
148 #ifdef __MIPSEL
149 /* On mipsel, jr ra needs to be followed by a nop.
150    0x03e00008 as a 64 bits integer just does that */
151 #define RETURN_INSTR_TYPE uint64_t
152 #endif
153 
154 #elif defined __s390__
155 #define RETURN_INSTR 0x07fe0000 /* br %r14 */
156 
157 #elif defined __aarch64__
158 #define RETURN_INSTR 0xd65f03c0 /* ret */
159 
160 #elif defined __ia64
161 struct ia64_instr { uint32_t mI[4]; };
162 static const ia64_instr _return_instr =
163   {{ 0x00000011, 0x00000001, 0x80000200, 0x00840008 }}; /* br.ret.sptk.many b0 */
164 
165 #define RETURN_INSTR _return_instr
166 #define RETURN_INSTR_TYPE ia64_instr
167 
168 #else
169 #error "Need return instruction for this architecture"
170 #endif
171 
172 #ifndef RETURN_INSTR_TYPE
173 #define RETURN_INSTR_TYPE uint32_t
174 #endif
175 
176 // Miscellaneous Windows/Unix portability gumph
177 
178 #ifdef _WIN32
179 // Uses of this function deliberately leak the string.
180 static LPSTR
StrW32Error(DWORD aErrcode)181 StrW32Error(DWORD aErrcode)
182 {
183   LPSTR errmsg;
184   FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
185                  FORMAT_MESSAGE_FROM_SYSTEM |
186                  FORMAT_MESSAGE_IGNORE_INSERTS,
187                  nullptr, aErrcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
188                  (LPSTR)&errmsg, 0, nullptr);
189 
190   // FormatMessage puts an unwanted newline at the end of the string
191   size_t n = strlen(errmsg)-1;
192   while (errmsg[n] == '\r' || errmsg[n] == '\n') {
193     n--;
194   }
195   errmsg[n+1] = '\0';
196   return errmsg;
197 }
198 #define LastErrMsg() (StrW32Error(GetLastError()))
199 
200 // Because we use VirtualAlloc in MEM_RESERVE mode, the "page size" we want
201 // is the allocation granularity.
202 static SYSTEM_INFO sInfo_;
203 
204 static inline uint32_t
PageSize()205 PageSize()
206 {
207   return sInfo_.dwAllocationGranularity;
208 }
209 
210 static void*
ReserveRegion(uintptr_t aRequest,bool aAccessible)211 ReserveRegion(uintptr_t aRequest, bool aAccessible)
212 {
213   return VirtualAlloc((void*)aRequest, PageSize(),
214                       aAccessible ? MEM_RESERVE|MEM_COMMIT : MEM_RESERVE,
215                       aAccessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS);
216 }
217 
218 static void
ReleaseRegion(void * aPage)219 ReleaseRegion(void* aPage)
220 {
221   VirtualFree(aPage, PageSize(), MEM_RELEASE);
222 }
223 
224 static bool
ProbeRegion(uintptr_t aPage)225 ProbeRegion(uintptr_t aPage)
226 {
227   return aPage >= (uintptr_t)sInfo_.lpMaximumApplicationAddress &&
228          aPage + PageSize() >= (uintptr_t)sInfo_.lpMaximumApplicationAddress;
229 }
230 
231 static bool
MakeRegionExecutable(void *)232 MakeRegionExecutable(void*)
233 {
234   return false;
235 }
236 
237 #undef MAP_FAILED
238 #define MAP_FAILED 0
239 
240 #else // Unix
241 
242 #define LastErrMsg() (strerror(errno))
243 
244 static unsigned long gUnixPageSize;
245 
246 static inline unsigned long
PageSize()247 PageSize()
248 {
249   return gUnixPageSize;
250 }
251 
252 static void*
ReserveRegion(uintptr_t aRequest,bool aAccessible)253 ReserveRegion(uintptr_t aRequest, bool aAccessible)
254 {
255   return mmap(reinterpret_cast<void*>(aRequest), PageSize(),
256               aAccessible ? PROT_READ|PROT_WRITE : PROT_NONE,
257               MAP_PRIVATE|MAP_ANON, -1, 0);
258 }
259 
260 static void
ReleaseRegion(void * aPage)261 ReleaseRegion(void* aPage)
262 {
263   munmap(aPage, PageSize());
264 }
265 
266 static bool
ProbeRegion(uintptr_t aPage)267 ProbeRegion(uintptr_t aPage)
268 {
269   return !!madvise(reinterpret_cast<void*>(aPage), PageSize(), MADV_NORMAL);
270 }
271 
272 static int
MakeRegionExecutable(void * aPage)273 MakeRegionExecutable(void* aPage)
274 {
275   return mprotect((caddr_t)aPage, PageSize(), PROT_READ|PROT_WRITE|PROT_EXEC);
276 }
277 
278 #endif
279 
280 static uintptr_t
ReservePoisonArea()281 ReservePoisonArea()
282 {
283   if (sizeof(uintptr_t) == 8) {
284     // Use the hardware-inaccessible region.
285     // We have to avoid 64-bit constants and shifts by 32 bits, since this
286     // code is compiled in 32-bit mode, although it is never executed there.
287     uintptr_t result = (((uintptr_t(0x7FFFFFFFu) << 31) << 1 |
288                          uintptr_t(0xF0DEAFFFu)) &
289                         ~uintptr_t(PageSize()-1));
290     printf("INFO | poison area assumed at 0x%.*" PRIxPTR "\n", SIZxPTR, result);
291     return result;
292   }
293 
294   // First see if we can allocate the preferred poison address from the OS.
295   uintptr_t candidate = (0xF0DEAFFF & ~(PageSize() - 1));
296   void* result = ReserveRegion(candidate, false);
297   if (result == reinterpret_cast<void*>(candidate)) {
298     // success - inaccessible page allocated
299     printf("INFO | poison area allocated at 0x%.*" PRIxPTR
300            " (preferred addr)\n", SIZxPTR, reinterpret_cast<uintptr_t>(result));
301     return candidate;
302   }
303 
304   // That didn't work, so see if the preferred address is within a range
305   // of permanently inacessible memory.
306   if (ProbeRegion(candidate)) {
307     // success - selected page cannot be usable memory
308     if (result != MAP_FAILED) {
309       ReleaseRegion(result);
310     }
311     printf("INFO | poison area assumed at 0x%.*" PRIxPTR
312            " (preferred addr)\n", SIZxPTR, candidate);
313     return candidate;
314   }
315 
316   // The preferred address is already in use.  Did the OS give us a
317   // consolation prize?
318   if (result != MAP_FAILED) {
319     uintptr_t ures = reinterpret_cast<uintptr_t>(result);
320     printf("INFO | poison area allocated at 0x%.*" PRIxPTR
321            " (consolation prize)\n", SIZxPTR, ures);
322     return ures;
323   }
324 
325   // It didn't, so try to allocate again, without any constraint on
326   // the address.
327   result = ReserveRegion(0, false);
328   if (result != MAP_FAILED) {
329     uintptr_t ures = reinterpret_cast<uintptr_t>(result);
330     printf("INFO | poison area allocated at 0x%.*" PRIxPTR
331            " (fallback)\n", SIZxPTR, ures);
332     return ures;
333   }
334 
335   printf("ERROR | no usable poison area found\n");
336   return 0;
337 }
338 
339 /* The "positive control" area confirms that we can allocate a page with the
340  * proper characteristics.
341  */
342 static uintptr_t
ReservePositiveControl()343 ReservePositiveControl()
344 {
345 
346   void* result = ReserveRegion(0, false);
347   if (result == MAP_FAILED) {
348     printf("ERROR | allocating positive control | %s\n", LastErrMsg());
349     return 0;
350   }
351   printf("INFO | positive control allocated at 0x%.*" PRIxPTR "\n",
352          SIZxPTR, (uintptr_t)result);
353   return (uintptr_t)result;
354 }
355 
356 /* The "negative control" area confirms that our probe logic does detect a
357  * page that is readable, writable, or executable.
358  */
359 static uintptr_t
ReserveNegativeControl()360 ReserveNegativeControl()
361 {
362   void* result = ReserveRegion(0, true);
363   if (result == MAP_FAILED) {
364     printf("ERROR | allocating negative control | %s\n", LastErrMsg());
365     return 0;
366   }
367 
368   // Fill the page with return instructions.
369   RETURN_INSTR_TYPE* p = reinterpret_cast<RETURN_INSTR_TYPE*>(result);
370   RETURN_INSTR_TYPE* limit =
371     reinterpret_cast<RETURN_INSTR_TYPE*>(
372       reinterpret_cast<char*>(result) + PageSize());
373   while (p < limit) {
374     *p++ = RETURN_INSTR;
375   }
376 
377   // Now mark it executable as well as readable and writable.
378   // (mmap(PROT_EXEC) may fail when applied to anonymous memory.)
379 
380   if (MakeRegionExecutable(result)) {
381     printf("ERROR | making negative control executable | %s\n", LastErrMsg());
382     return 0;
383   }
384 
385   printf("INFO | negative control allocated at 0x%.*" PRIxPTR "\n",
386          SIZxPTR, (uintptr_t)result);
387   return (uintptr_t)result;
388 }
389 
390 static void
JumpTo(uintptr_t aOpaddr)391 JumpTo(uintptr_t aOpaddr)
392 {
393 #ifdef __ia64
394   struct func_call
395   {
396     uintptr_t mFunc;
397     uintptr_t mGp;
398   } call = { aOpaddr, };
399   ((void (*)())&call)();
400 #else
401   ((void (*)())aOpaddr)();
402 #endif
403 }
404 
405 #ifdef _WIN32
406 static BOOL
IsBadExecPtr(uintptr_t aPtr)407 IsBadExecPtr(uintptr_t aPtr)
408 {
409   BOOL ret = false;
410 
411 #ifdef _MSC_VER
412   __try {
413     JumpTo(aPtr);
414   } __except (EXCEPTION_EXECUTE_HANDLER) {
415     ret = true;
416   }
417 #else
418   printf("INFO | exec test not supported on MinGW build\n");
419   // We do our best
420   ret = IsBadReadPtr((const void*)aPtr, 1);
421 #endif
422   return ret;
423 }
424 #endif
425 
426 /* Test each page.  */
427 static bool
TestPage(const char * aPageLabel,uintptr_t aPageAddr,int aShouldSucceed)428 TestPage(const char* aPageLabel, uintptr_t aPageAddr, int aShouldSucceed)
429 {
430   const char* oplabel;
431   uintptr_t opaddr;
432 
433   bool failed = false;
434   for (unsigned int test = 0; test < 3; test++) {
435     switch (test) {
436       // The execute test must be done before the write test, because the
437       // write test will clobber memory at the target address.
438     case 0: oplabel = "reading"; opaddr = aPageAddr + PageSize()/2 - 1; break;
439     case 1: oplabel = "executing"; opaddr = aPageAddr + PageSize()/2; break;
440     case 2: oplabel = "writing"; opaddr = aPageAddr + PageSize()/2 - 1; break;
441     default: abort();
442     }
443 
444 #ifdef _WIN32
445     BOOL badptr;
446 
447     switch (test) {
448     case 0: badptr = IsBadReadPtr((const void*)opaddr, 1); break;
449     case 1: badptr = IsBadExecPtr(opaddr); break;
450     case 2: badptr = IsBadWritePtr((void*)opaddr, 1); break;
451     default: abort();
452     }
453 
454     if (badptr) {
455       if (aShouldSucceed) {
456         printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, aPageLabel);
457         failed = true;
458       } else {
459         printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
460       }
461     } else {
462       // if control reaches this point the probe succeeded
463       if (aShouldSucceed) {
464         printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
465       } else {
466         printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, aPageLabel);
467         failed = true;
468       }
469     }
470 #else
471     pid_t pid = fork();
472     if (pid == -1) {
473       printf("ERROR | %s %s | fork=%s\n", oplabel, aPageLabel,
474              LastErrMsg());
475       exit(2);
476     } else if (pid == 0) {
477       volatile unsigned char scratch;
478       switch (test) {
479       case 0: scratch = *(volatile unsigned char*)opaddr; break;
480       case 1: JumpTo(opaddr); break;
481       case 2: *(volatile unsigned char*)opaddr = 0; break;
482       default: abort();
483       }
484       (void)scratch;
485       _exit(0);
486     } else {
487       int status;
488       if (waitpid(pid, &status, 0) != pid) {
489         printf("ERROR | %s %s | wait=%s\n", oplabel, aPageLabel,
490                LastErrMsg());
491         exit(2);
492       }
493 
494       if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
495         if (aShouldSucceed) {
496           printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
497         } else {
498           printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected successful exit\n",
499                  oplabel, aPageLabel);
500           failed = true;
501         }
502       } else if (WIFEXITED(status)) {
503         printf("ERROR | %s %s | unexpected exit code %d\n",
504                oplabel, aPageLabel, WEXITSTATUS(status));
505         exit(2);
506       } else if (WIFSIGNALED(status)) {
507         if (aShouldSucceed) {
508           printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected signal %d\n",
509                  oplabel, aPageLabel, WTERMSIG(status));
510           failed = true;
511         } else {
512           printf("TEST-PASS | %s %s | signal %d (as expected)\n",
513                  oplabel, aPageLabel, WTERMSIG(status));
514         }
515       } else {
516         printf("ERROR | %s %s | unexpected exit status %d\n",
517                oplabel, aPageLabel, status);
518         exit(2);
519       }
520     }
521 #endif
522   }
523   return failed;
524 }
525 
526 int
main()527 main()
528 {
529 #ifdef _WIN32
530   GetSystemInfo(&sInfo_);
531 #else
532   gUnixPageSize = sysconf(_SC_PAGESIZE);
533 #endif
534 
535   uintptr_t ncontrol = ReserveNegativeControl();
536   uintptr_t pcontrol = ReservePositiveControl();
537   uintptr_t poison = ReservePoisonArea();
538 
539   if (!ncontrol || !pcontrol || !poison) {
540     return 2;
541   }
542 
543   bool failed = false;
544   failed |= TestPage("negative control", ncontrol, 1);
545   failed |= TestPage("positive control", pcontrol, 0);
546   failed |= TestPage("poison area", poison, 0);
547 
548   return failed ? 1 : 0;
549 }
550