forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			556 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			556 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | |
|  */
 | |
| 
 | |
| /* Code in this file needs to be kept in sync with code in nsPresArena.cpp.
 | |
|  *
 | |
|  * We want to use a fixed address for frame poisoning so that it is readily
 | |
|  * identifiable in crash dumps.  Whether such an address is available
 | |
|  * without any special setup depends on the system configuration.
 | |
|  *
 | |
|  * All current 64-bit CPUs (with the possible exception of PowerPC64)
 | |
|  * reserve the vast majority of the virtual address space for future
 | |
|  * hardware extensions; valid addresses must be below some break point
 | |
|  * between 2**48 and 2**54, depending on exactly which chip you have.  Some
 | |
|  * chips (notably amd64) also allow the use of the *highest* 2**48 -- 2**54
 | |
|  * addresses.  Thus, if user space pointers are 64 bits wide, we can just
 | |
|  * use an address outside this range, and no more is required.  To
 | |
|  * accommodate the chips that allow very high addresses to be valid, the
 | |
|  * value chosen is close to 2**63 (that is, in the middle of the space).
 | |
|  *
 | |
|  * In most cases, a purely 32-bit operating system must reserve some
 | |
|  * fraction of the address space for its own use.  Contemporary 32-bit OSes
 | |
|  * tend to take the high gigabyte or so (0xC000_0000 on up).  If we can
 | |
|  * prove that high addresses are reserved to the kernel, we can use an
 | |
|  * address in that region.  Unfortunately, not all 32-bit OSes do this;
 | |
|  * OSX 10.4 might not, and it is unclear what mobile OSes are like
 | |
|  * (some 32-bit CPUs make it very easy for the kernel to exist in its own
 | |
|  * private address space).
 | |
|  *
 | |
|  * Furthermore, when a 32-bit user space process is running on a 64-bit
 | |
|  * kernel, the operating system has no need to reserve any of the space that
 | |
|  * the process can see, and generally does not do so.  This is the scenario
 | |
|  * of greatest concern, since it covers all contemporary OSX iterations
 | |
|  * (10.5+) as well as Windows Vista and 7 on newer amd64 hardware.  Linux on
 | |
|  * amd64 is generally run as a pure 64-bit environment, but its 32-bit
 | |
|  * compatibility mode also has this property.
 | |
|  *
 | |
|  * Thus, when user space pointers are 32 bits wide, we need to validate
 | |
|  * our chosen address, and possibly *make* it a good poison address by
 | |
|  * allocating a page around it and marking it inaccessible.  The algorithm
 | |
|  * for this is:
 | |
|  *
 | |
|  *  1. Attempt to make the page surrounding the poison address a reserved,
 | |
|  *     inaccessible memory region using OS primitives.  On Windows, this is
 | |
|  *     done with VirtualAlloc(MEM_RESERVE); on Unix, mmap(PROT_NONE).
 | |
|  *
 | |
|  *  2. If mmap/VirtualAlloc failed, there are two possible reasons: either
 | |
|  *     the region is reserved to the kernel and no further action is
 | |
|  *     required, or there is already usable memory in this area and we have
 | |
|  *     to pick a different address.  The tricky part is knowing which case
 | |
|  *     we have, without attempting to access the region.  On Windows, we
 | |
|  *     rely on GetSystemInfo()'s reported upper and lower bounds of the
 | |
|  *     application memory area.  On Unix, there is nothing devoted to the
 | |
|  *     purpose, but seeing if madvise() fails is close enough (it *might*
 | |
|  *     disrupt someone else's use of the memory region, but not by as much
 | |
|  *     as anything else available).
 | |
|  *
 | |
|  * Be aware of these gotchas:
 | |
|  *
 | |
|  * 1. We cannot use mmap() with MAP_FIXED.  MAP_FIXED is defined to
 | |
|  *    _replace_ any existing mapping in the region, if necessary to satisfy
 | |
|  *    the request.  Obviously, as we are blindly attempting to acquire a
 | |
|  *    page at a constant address, we must not do this, lest we overwrite
 | |
|  *    someone else's allocation.
 | |
|  *
 | |
|  * 2. For the same reason, we cannot blindly use mprotect() if mmap() fails.
 | |
|  *
 | |
|  * 3. madvise() may fail when applied to a 'magic' memory region provided as
 | |
|  *    a kernel/user interface.  Fortunately, the only such case I know about
 | |
|  *    is the "vsyscall" area (not to be confused with the "vdso" area) for
 | |
|  *    *64*-bit processes on Linux - and we don't even run this code for
 | |
|  *    64-bit processes.
 | |
|  *
 | |
|  * 4. VirtualQuery() does not produce any useful information if
 | |
|  *    applied to kernel memory - in fact, it doesn't write its output
 | |
|  *    at all.  Thus, it is not used here.
 | |
|  */
 | |
| 
 | |
| #include "mozilla/IntegerPrintfMacros.h"
 | |
| 
 | |
| // MAP_ANON(YMOUS) is not in any standard.  Add defines as necessary.
 | |
| #define _GNU_SOURCE 1
 | |
| #define _DARWIN_C_SOURCE 1
 | |
| 
 | |
| #include <stddef.h>
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #ifdef _WIN32
 | |
| #include <windows.h>
 | |
| #else
 | |
| #include <sys/types.h>
 | |
| #include <fcntl.h>
 | |
| #include <signal.h>
 | |
| #include <unistd.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/wait.h>
 | |
| 
 | |
| #include <sys/mman.h>
 | |
| #ifndef MAP_ANON
 | |
| #ifdef MAP_ANONYMOUS
 | |
| #define MAP_ANON MAP_ANONYMOUS
 | |
| #else
 | |
| #error "Don't know how to get anonymous memory"
 | |
| #endif
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| #define SIZxPTR ((int)(sizeof(uintptr_t)*2))
 | |
| 
 | |
| /* This program assumes that a whole number of return instructions fit into
 | |
|  * 32 bits, and that 32-bit alignment is sufficient for a branch destination.
 | |
|  * For architectures where this is not true, fiddling with RETURN_INSTR_TYPE
 | |
|  * can be enough.
 | |
|  */
 | |
| 
 | |
| #if defined __i386__ || defined __x86_64__ ||   \
 | |
|   defined __i386 || defined __x86_64 ||         \
 | |
|   defined _M_IX86 || defined _M_AMD64
 | |
| #define RETURN_INSTR 0xC3C3C3C3  /* ret; ret; ret; ret */
 | |
| 
 | |
| #elif defined __arm__ || defined _M_ARM
 | |
| #define RETURN_INSTR 0xE12FFF1E /* bx lr */
 | |
| 
 | |
| // PPC has its own style of CPU-id #defines.  There is no Windows for
 | |
| // PPC as far as I know, so no _M_ variant.
 | |
| #elif defined _ARCH_PPC || defined _ARCH_PWR || defined _ARCH_PWR2
 | |
| #define RETURN_INSTR 0x4E800020 /* blr */
 | |
| 
 | |
| #elif defined __sparc || defined __sparcv9
 | |
| #define RETURN_INSTR 0x81c3e008 /* retl */
 | |
| 
 | |
| #elif defined __alpha
 | |
| #define RETURN_INSTR 0x6bfa8001 /* ret */
 | |
| 
 | |
| #elif defined __hppa
 | |
| #define RETURN_INSTR 0xe840c002 /* bv,n r0(rp) */
 | |
| 
 | |
| #elif defined __mips
 | |
| #define RETURN_INSTR 0x03e00008 /* jr ra */
 | |
| 
 | |
| #ifdef __MIPSEL
 | |
| /* On mipsel, jr ra needs to be followed by a nop.
 | |
|    0x03e00008 as a 64 bits integer just does that */
 | |
| #define RETURN_INSTR_TYPE uint64_t
 | |
| #endif
 | |
| 
 | |
| #elif defined __s390__
 | |
| #define RETURN_INSTR 0x07fe0000 /* br %r14 */
 | |
| 
 | |
| #elif defined __sh__
 | |
| #define RETURN_INSTR 0x0b000b00 /* rts; rts */
 | |
| 
 | |
| #elif defined __aarch64__
 | |
| #define RETURN_INSTR 0xd65f03c0 /* ret */
 | |
| 
 | |
| #elif defined __ia64
 | |
| struct ia64_instr { uint32_t mI[4]; };
 | |
| static const ia64_instr _return_instr =
 | |
|   {{ 0x00000011, 0x00000001, 0x80000200, 0x00840008 }}; /* br.ret.sptk.many b0 */
 | |
| 
 | |
| #define RETURN_INSTR _return_instr
 | |
| #define RETURN_INSTR_TYPE ia64_instr
 | |
| 
 | |
| #else
 | |
| #error "Need return instruction for this architecture"
 | |
| #endif
 | |
| 
 | |
| #ifndef RETURN_INSTR_TYPE
 | |
| #define RETURN_INSTR_TYPE uint32_t
 | |
| #endif
 | |
| 
 | |
| // Miscellaneous Windows/Unix portability gumph
 | |
| 
 | |
| #ifdef _WIN32
 | |
| // Uses of this function deliberately leak the string.
 | |
| static LPSTR
 | |
| StrW32Error(DWORD aErrcode)
 | |
| {
 | |
|   LPSTR errmsg;
 | |
|   FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
 | |
|                  FORMAT_MESSAGE_FROM_SYSTEM |
 | |
|                  FORMAT_MESSAGE_IGNORE_INSERTS,
 | |
|                  nullptr, aErrcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | |
|                  (LPSTR)&errmsg, 0, nullptr);
 | |
| 
 | |
|   // FormatMessage puts an unwanted newline at the end of the string
 | |
|   size_t n = strlen(errmsg)-1;
 | |
|   while (errmsg[n] == '\r' || errmsg[n] == '\n') {
 | |
|     n--;
 | |
|   }
 | |
|   errmsg[n+1] = '\0';
 | |
|   return errmsg;
 | |
| }
 | |
| #define LastErrMsg() (StrW32Error(GetLastError()))
 | |
| 
 | |
| // Because we use VirtualAlloc in MEM_RESERVE mode, the "page size" we want
 | |
| // is the allocation granularity.
 | |
| static SYSTEM_INFO sInfo_;
 | |
| 
 | |
| static inline uint32_t
 | |
| PageSize()
 | |
| {
 | |
|   return sInfo_.dwAllocationGranularity;
 | |
| }
 | |
| 
 | |
| static void*
 | |
| ReserveRegion(uintptr_t aRequest, bool aAccessible)
 | |
| {
 | |
|   return VirtualAlloc((void*)aRequest, PageSize(),
 | |
|                       aAccessible ? MEM_RESERVE|MEM_COMMIT : MEM_RESERVE,
 | |
|                       aAccessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS);
 | |
| }
 | |
| 
 | |
| static void
 | |
| ReleaseRegion(void* aPage)
 | |
| {
 | |
|   VirtualFree(aPage, PageSize(), MEM_RELEASE);
 | |
| }
 | |
| 
 | |
| static bool
 | |
| ProbeRegion(uintptr_t aPage)
 | |
| {
 | |
|   return aPage >= (uintptr_t)sInfo_.lpMaximumApplicationAddress &&
 | |
|          aPage + PageSize() >= (uintptr_t)sInfo_.lpMaximumApplicationAddress;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| MakeRegionExecutable(void*)
 | |
| {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| #undef MAP_FAILED
 | |
| #define MAP_FAILED 0
 | |
| 
 | |
| #else // Unix
 | |
| 
 | |
| #define LastErrMsg() (strerror(errno))
 | |
| 
 | |
| static unsigned long gUnixPageSize;
 | |
| 
 | |
| static inline unsigned long
 | |
| PageSize()
 | |
| {
 | |
|   return gUnixPageSize;
 | |
| }
 | |
| 
 | |
| static void*
 | |
| ReserveRegion(uintptr_t aRequest, bool aAccessible)
 | |
| {
 | |
|   return mmap(reinterpret_cast<void*>(aRequest), PageSize(),
 | |
|               aAccessible ? PROT_READ|PROT_WRITE : PROT_NONE,
 | |
|               MAP_PRIVATE|MAP_ANON, -1, 0);
 | |
| }
 | |
| 
 | |
| static void
 | |
| ReleaseRegion(void* aPage)
 | |
| {
 | |
|   munmap(aPage, PageSize());
 | |
| }
 | |
| 
 | |
| static bool
 | |
| ProbeRegion(uintptr_t aPage)
 | |
| {
 | |
| #ifdef XP_SOLARIS
 | |
|   return !!posix_madvise(reinterpret_cast<void*>(aPage), PageSize(), POSIX_MADV_NORMAL);
 | |
| #else
 | |
|   return !!madvise(reinterpret_cast<void*>(aPage), PageSize(), MADV_NORMAL);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static int
 | |
| MakeRegionExecutable(void* aPage)
 | |
| {
 | |
|   return mprotect((caddr_t)aPage, PageSize(), PROT_READ|PROT_WRITE|PROT_EXEC);
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| static uintptr_t
 | |
| ReservePoisonArea()
 | |
| {
 | |
|   if (sizeof(uintptr_t) == 8) {
 | |
|     // Use the hardware-inaccessible region.
 | |
|     // We have to avoid 64-bit constants and shifts by 32 bits, since this
 | |
|     // code is compiled in 32-bit mode, although it is never executed there.
 | |
|     uintptr_t result = (((uintptr_t(0x7FFFFFFFu) << 31) << 1 |
 | |
|                          uintptr_t(0xF0DEAFFFu)) &
 | |
|                         ~uintptr_t(PageSize()-1));
 | |
|     printf("INFO | poison area assumed at 0x%.*" PRIxPTR "\n", SIZxPTR, result);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   // First see if we can allocate the preferred poison address from the OS.
 | |
|   uintptr_t candidate = (0xF0DEAFFF & ~(PageSize() - 1));
 | |
|   void* result = ReserveRegion(candidate, false);
 | |
|   if (result == reinterpret_cast<void*>(candidate)) {
 | |
|     // success - inaccessible page allocated
 | |
|     printf("INFO | poison area allocated at 0x%.*" PRIxPTR
 | |
|            " (preferred addr)\n", SIZxPTR, reinterpret_cast<uintptr_t>(result));
 | |
|     return candidate;
 | |
|   }
 | |
| 
 | |
|   // That didn't work, so see if the preferred address is within a range
 | |
|   // of permanently inacessible memory.
 | |
|   if (ProbeRegion(candidate)) {
 | |
|     // success - selected page cannot be usable memory
 | |
|     if (result != MAP_FAILED) {
 | |
|       ReleaseRegion(result);
 | |
|     }
 | |
|     printf("INFO | poison area assumed at 0x%.*" PRIxPTR
 | |
|            " (preferred addr)\n", SIZxPTR, candidate);
 | |
|     return candidate;
 | |
|   }
 | |
| 
 | |
|   // The preferred address is already in use.  Did the OS give us a
 | |
|   // consolation prize?
 | |
|   if (result != MAP_FAILED) {
 | |
|     uintptr_t ures = reinterpret_cast<uintptr_t>(result);
 | |
|     printf("INFO | poison area allocated at 0x%.*" PRIxPTR
 | |
|            " (consolation prize)\n", SIZxPTR, ures);
 | |
|     return ures;
 | |
|   }
 | |
| 
 | |
|   // It didn't, so try to allocate again, without any constraint on
 | |
|   // the address.
 | |
|   result = ReserveRegion(0, false);
 | |
|   if (result != MAP_FAILED) {
 | |
|     uintptr_t ures = reinterpret_cast<uintptr_t>(result);
 | |
|     printf("INFO | poison area allocated at 0x%.*" PRIxPTR
 | |
|            " (fallback)\n", SIZxPTR, ures);
 | |
|     return ures;
 | |
|   }
 | |
| 
 | |
|   printf("ERROR | no usable poison area found\n");
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* The "positive control" area confirms that we can allocate a page with the
 | |
|  * proper characteristics.
 | |
|  */
 | |
| static uintptr_t
 | |
| ReservePositiveControl()
 | |
| {
 | |
| 
 | |
|   void* result = ReserveRegion(0, false);
 | |
|   if (result == MAP_FAILED) {
 | |
|     printf("ERROR | allocating positive control | %s\n", LastErrMsg());
 | |
|     return 0;
 | |
|   }
 | |
|   printf("INFO | positive control allocated at 0x%.*" PRIxPTR "\n",
 | |
|          SIZxPTR, (uintptr_t)result);
 | |
|   return (uintptr_t)result;
 | |
| }
 | |
| 
 | |
| /* The "negative control" area confirms that our probe logic does detect a
 | |
|  * page that is readable, writable, or executable.
 | |
|  */
 | |
| static uintptr_t
 | |
| ReserveNegativeControl()
 | |
| {
 | |
|   void* result = ReserveRegion(0, true);
 | |
|   if (result == MAP_FAILED) {
 | |
|     printf("ERROR | allocating negative control | %s\n", LastErrMsg());
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   // Fill the page with return instructions.
 | |
|   RETURN_INSTR_TYPE* p = reinterpret_cast<RETURN_INSTR_TYPE*>(result);
 | |
|   RETURN_INSTR_TYPE* limit =
 | |
|     reinterpret_cast<RETURN_INSTR_TYPE*>(
 | |
|       reinterpret_cast<char*>(result) + PageSize());
 | |
|   while (p < limit) {
 | |
|     *p++ = RETURN_INSTR;
 | |
|   }
 | |
| 
 | |
|   // Now mark it executable as well as readable and writable.
 | |
|   // (mmap(PROT_EXEC) may fail when applied to anonymous memory.)
 | |
| 
 | |
|   if (MakeRegionExecutable(result)) {
 | |
|     printf("ERROR | making negative control executable | %s\n", LastErrMsg());
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   printf("INFO | negative control allocated at 0x%.*" PRIxPTR "\n",
 | |
|          SIZxPTR, (uintptr_t)result);
 | |
|   return (uintptr_t)result;
 | |
| }
 | |
| 
 | |
| static void
 | |
| JumpTo(uintptr_t aOpaddr)
 | |
| {
 | |
| #ifdef __ia64
 | |
|   struct func_call
 | |
|   {
 | |
|     uintptr_t mFunc;
 | |
|     uintptr_t mGp;
 | |
|   } call = { aOpaddr, };
 | |
|   ((void (*)())&call)();
 | |
| #else
 | |
|   ((void (*)())aOpaddr)();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| #ifdef _WIN32
 | |
| static BOOL
 | |
| IsBadExecPtr(uintptr_t aPtr)
 | |
| {
 | |
|   BOOL ret = false;
 | |
| 
 | |
| #ifdef _MSC_VER
 | |
|   __try {
 | |
|     JumpTo(aPtr);
 | |
|   } __except (EXCEPTION_EXECUTE_HANDLER) {
 | |
|     ret = true;
 | |
|   }
 | |
| #else
 | |
|   printf("INFO | exec test not supported on MinGW build\n");
 | |
|   // We do our best
 | |
|   ret = IsBadReadPtr((const void*)aPtr, 1);
 | |
| #endif
 | |
|   return ret;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* Test each page.  */
 | |
| static bool
 | |
| TestPage(const char* aPageLabel, uintptr_t aPageAddr, int aShouldSucceed)
 | |
| {
 | |
|   const char* oplabel;
 | |
|   uintptr_t opaddr;
 | |
| 
 | |
|   bool failed = false;
 | |
|   for (unsigned int test = 0; test < 3; test++) {
 | |
|     switch (test) {
 | |
|       // The execute test must be done before the write test, because the
 | |
|       // write test will clobber memory at the target address.
 | |
|     case 0: oplabel = "reading"; opaddr = aPageAddr + PageSize()/2 - 1; break;
 | |
|     case 1: oplabel = "executing"; opaddr = aPageAddr + PageSize()/2; break;
 | |
|     case 2: oplabel = "writing"; opaddr = aPageAddr + PageSize()/2 - 1; break;
 | |
|     default: abort();
 | |
|     }
 | |
| 
 | |
| #ifdef _WIN32
 | |
|     BOOL badptr;
 | |
| 
 | |
|     switch (test) {
 | |
|     case 0: badptr = IsBadReadPtr((const void*)opaddr, 1); break;
 | |
|     case 1: badptr = IsBadExecPtr(opaddr); break;
 | |
|     case 2: badptr = IsBadWritePtr((void*)opaddr, 1); break;
 | |
|     default: abort();
 | |
|     }
 | |
| 
 | |
|     if (badptr) {
 | |
|       if (aShouldSucceed) {
 | |
|         printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, aPageLabel);
 | |
|         failed = true;
 | |
|       } else {
 | |
|         printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
 | |
|       }
 | |
|     } else {
 | |
|       // if control reaches this point the probe succeeded
 | |
|       if (aShouldSucceed) {
 | |
|         printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
 | |
|       } else {
 | |
|         printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, aPageLabel);
 | |
|         failed = true;
 | |
|       }
 | |
|     }
 | |
| #else
 | |
|     pid_t pid = fork();
 | |
|     if (pid == -1) {
 | |
|       printf("ERROR | %s %s | fork=%s\n", oplabel, aPageLabel,
 | |
|              LastErrMsg());
 | |
|       exit(2);
 | |
|     } else if (pid == 0) {
 | |
|       volatile unsigned char scratch;
 | |
|       switch (test) {
 | |
|       case 0: scratch = *(volatile unsigned char*)opaddr; break;
 | |
|       case 1: JumpTo(opaddr); break;
 | |
|       case 2: *(volatile unsigned char*)opaddr = 0; break;
 | |
|       default: abort();
 | |
|       }
 | |
|       (void)scratch;
 | |
|       _exit(0);
 | |
|     } else {
 | |
|       int status;
 | |
|       if (waitpid(pid, &status, 0) != pid) {
 | |
|         printf("ERROR | %s %s | wait=%s\n", oplabel, aPageLabel,
 | |
|                LastErrMsg());
 | |
|         exit(2);
 | |
|       }
 | |
| 
 | |
|       if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
 | |
|         if (aShouldSucceed) {
 | |
|           printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
 | |
|         } else {
 | |
|           printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected successful exit\n",
 | |
|                  oplabel, aPageLabel);
 | |
|           failed = true;
 | |
|         }
 | |
|       } else if (WIFEXITED(status)) {
 | |
|         printf("ERROR | %s %s | unexpected exit code %d\n",
 | |
|                oplabel, aPageLabel, WEXITSTATUS(status));
 | |
|         exit(2);
 | |
|       } else if (WIFSIGNALED(status)) {
 | |
|         if (aShouldSucceed) {
 | |
|           printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected signal %d\n",
 | |
|                  oplabel, aPageLabel, WTERMSIG(status));
 | |
|           failed = true;
 | |
|         } else {
 | |
|           printf("TEST-PASS | %s %s | signal %d (as expected)\n",
 | |
|                  oplabel, aPageLabel, WTERMSIG(status));
 | |
|         }
 | |
|       } else {
 | |
|         printf("ERROR | %s %s | unexpected exit status %d\n",
 | |
|                oplabel, aPageLabel, status);
 | |
|         exit(2);
 | |
|       }
 | |
|     }
 | |
| #endif
 | |
|   }
 | |
|   return failed;
 | |
| }
 | |
| 
 | |
| int
 | |
| main()
 | |
| {
 | |
| #ifdef _WIN32
 | |
|   GetSystemInfo(&sInfo_);
 | |
| #else
 | |
|   gUnixPageSize = sysconf(_SC_PAGESIZE);
 | |
| #endif
 | |
| 
 | |
|   uintptr_t ncontrol = ReserveNegativeControl();
 | |
|   uintptr_t pcontrol = ReservePositiveControl();
 | |
|   uintptr_t poison = ReservePoisonArea();
 | |
| 
 | |
|   if (!ncontrol || !pcontrol || !poison) {
 | |
|     return 2;
 | |
|   }
 | |
| 
 | |
|   bool failed = false;
 | |
|   failed |= TestPage("negative control", ncontrol, 1);
 | |
|   failed |= TestPage("positive control", pcontrol, 0);
 | |
|   failed |= TestPage("poison area", poison, 0);
 | |
| 
 | |
|   return failed ? 1 : 0;
 | |
| }
 | 
