mfbt/tests/TestPoisonArea.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mfbt/tests/TestPoisonArea.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,540 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.9 + */
    1.10 +
    1.11 +/* Code in this file needs to be kept in sync with code in nsPresArena.cpp.
    1.12 + *
    1.13 + * We want to use a fixed address for frame poisoning so that it is readily
    1.14 + * identifiable in crash dumps.  Whether such an address is available
    1.15 + * without any special setup depends on the system configuration.
    1.16 + *
    1.17 + * All current 64-bit CPUs (with the possible exception of PowerPC64)
    1.18 + * reserve the vast majority of the virtual address space for future
    1.19 + * hardware extensions; valid addresses must be below some break point
    1.20 + * between 2**48 and 2**54, depending on exactly which chip you have.  Some
    1.21 + * chips (notably amd64) also allow the use of the *highest* 2**48 -- 2**54
    1.22 + * addresses.  Thus, if user space pointers are 64 bits wide, we can just
    1.23 + * use an address outside this range, and no more is required.  To
    1.24 + * accommodate the chips that allow very high addresses to be valid, the
    1.25 + * value chosen is close to 2**63 (that is, in the middle of the space).
    1.26 + *
    1.27 + * In most cases, a purely 32-bit operating system must reserve some
    1.28 + * fraction of the address space for its own use.  Contemporary 32-bit OSes
    1.29 + * tend to take the high gigabyte or so (0xC000_0000 on up).  If we can
    1.30 + * prove that high addresses are reserved to the kernel, we can use an
    1.31 + * address in that region.  Unfortunately, not all 32-bit OSes do this;
    1.32 + * OSX 10.4 might not, and it is unclear what mobile OSes are like
    1.33 + * (some 32-bit CPUs make it very easy for the kernel to exist in its own
    1.34 + * private address space).
    1.35 + *
    1.36 + * Furthermore, when a 32-bit user space process is running on a 64-bit
    1.37 + * kernel, the operating system has no need to reserve any of the space that
    1.38 + * the process can see, and generally does not do so.  This is the scenario
    1.39 + * of greatest concern, since it covers all contemporary OSX iterations
    1.40 + * (10.5+) as well as Windows Vista and 7 on newer amd64 hardware.  Linux on
    1.41 + * amd64 is generally run as a pure 64-bit environment, but its 32-bit
    1.42 + * compatibility mode also has this property.
    1.43 + *
    1.44 + * Thus, when user space pointers are 32 bits wide, we need to validate
    1.45 + * our chosen address, and possibly *make* it a good poison address by
    1.46 + * allocating a page around it and marking it inaccessible.  The algorithm
    1.47 + * for this is:
    1.48 + *
    1.49 + *  1. Attempt to make the page surrounding the poison address a reserved,
    1.50 + *     inaccessible memory region using OS primitives.  On Windows, this is
    1.51 + *     done with VirtualAlloc(MEM_RESERVE); on Unix, mmap(PROT_NONE).
    1.52 + *
    1.53 + *  2. If mmap/VirtualAlloc failed, there are two possible reasons: either
    1.54 + *     the region is reserved to the kernel and no further action is
    1.55 + *     required, or there is already usable memory in this area and we have
    1.56 + *     to pick a different address.  The tricky part is knowing which case
    1.57 + *     we have, without attempting to access the region.  On Windows, we
    1.58 + *     rely on GetSystemInfo()'s reported upper and lower bounds of the
    1.59 + *     application memory area.  On Unix, there is nothing devoted to the
    1.60 + *     purpose, but seeing if madvise() fails is close enough (it *might*
    1.61 + *     disrupt someone else's use of the memory region, but not by as much
    1.62 + *     as anything else available).
    1.63 + *
    1.64 + * Be aware of these gotchas:
    1.65 + *
    1.66 + * 1. We cannot use mmap() with MAP_FIXED.  MAP_FIXED is defined to
    1.67 + *    _replace_ any existing mapping in the region, if necessary to satisfy
    1.68 + *    the request.  Obviously, as we are blindly attempting to acquire a
    1.69 + *    page at a constant address, we must not do this, lest we overwrite
    1.70 + *    someone else's allocation.
    1.71 + *
    1.72 + * 2. For the same reason, we cannot blindly use mprotect() if mmap() fails.
    1.73 + *
    1.74 + * 3. madvise() may fail when applied to a 'magic' memory region provided as
    1.75 + *    a kernel/user interface.  Fortunately, the only such case I know about
    1.76 + *    is the "vsyscall" area (not to be confused with the "vdso" area) for
    1.77 + *    *64*-bit processes on Linux - and we don't even run this code for
    1.78 + *    64-bit processes.
    1.79 + *
    1.80 + * 4. VirtualQuery() does not produce any useful information if
    1.81 + *    applied to kernel memory - in fact, it doesn't write its output
    1.82 + *    at all.  Thus, it is not used here.
    1.83 + */
    1.84 +
    1.85 +#include "mozilla/IntegerPrintfMacros.h"
    1.86 +#include "mozilla/NullPtr.h"
    1.87 +
    1.88 +// MAP_ANON(YMOUS) is not in any standard.  Add defines as necessary.
    1.89 +#define _GNU_SOURCE 1
    1.90 +#define _DARWIN_C_SOURCE 1
    1.91 +
    1.92 +#include <stddef.h>
    1.93 +
    1.94 +#include <errno.h>
    1.95 +#include <stdio.h>
    1.96 +#include <stdlib.h>
    1.97 +#include <string.h>
    1.98 +
    1.99 +#ifdef _WIN32
   1.100 +#include <windows.h>
   1.101 +#else
   1.102 +#include <sys/types.h>
   1.103 +#include <fcntl.h>
   1.104 +#include <signal.h>
   1.105 +#include <unistd.h>
   1.106 +#include <sys/stat.h>
   1.107 +#include <sys/wait.h>
   1.108 +
   1.109 +#include <sys/mman.h>
   1.110 +#ifndef MAP_ANON
   1.111 +#ifdef MAP_ANONYMOUS
   1.112 +#define MAP_ANON MAP_ANONYMOUS
   1.113 +#else
   1.114 +#error "Don't know how to get anonymous memory"
   1.115 +#endif
   1.116 +#endif
   1.117 +#endif
   1.118 +
   1.119 +#define SIZxPTR ((int)(sizeof(uintptr_t)*2))
   1.120 +
   1.121 +/* This program assumes that a whole number of return instructions fit into
   1.122 + * 32 bits, and that 32-bit alignment is sufficient for a branch destination.
   1.123 + * For architectures where this is not true, fiddling with RETURN_INSTR_TYPE
   1.124 + * can be enough.
   1.125 + */
   1.126 +
   1.127 +#if defined __i386__ || defined __x86_64__ ||   \
   1.128 +  defined __i386 || defined __x86_64 ||         \
   1.129 +  defined _M_IX86 || defined _M_AMD64
   1.130 +#define RETURN_INSTR 0xC3C3C3C3  /* ret; ret; ret; ret */
   1.131 +
   1.132 +#elif defined __arm__ || defined _M_ARM
   1.133 +#define RETURN_INSTR 0xE12FFF1E /* bx lr */
   1.134 +
   1.135 +// PPC has its own style of CPU-id #defines.  There is no Windows for
   1.136 +// PPC as far as I know, so no _M_ variant.
   1.137 +#elif defined _ARCH_PPC || defined _ARCH_PWR || defined _ARCH_PWR2
   1.138 +#define RETURN_INSTR 0x4E800020 /* blr */
   1.139 +
   1.140 +#elif defined __sparc || defined __sparcv9
   1.141 +#define RETURN_INSTR 0x81c3e008 /* retl */
   1.142 +
   1.143 +#elif defined __alpha
   1.144 +#define RETURN_INSTR 0x6bfa8001 /* ret */
   1.145 +
   1.146 +#elif defined __hppa
   1.147 +#define RETURN_INSTR 0xe840c002 /* bv,n r0(rp) */
   1.148 +
   1.149 +#elif defined __mips
   1.150 +#define RETURN_INSTR 0x03e00008 /* jr ra */
   1.151 +
   1.152 +#ifdef __MIPSEL
   1.153 +/* On mipsel, jr ra needs to be followed by a nop.
   1.154 +   0x03e00008 as a 64 bits integer just does that */
   1.155 +#define RETURN_INSTR_TYPE uint64_t
   1.156 +#endif
   1.157 +
   1.158 +#elif defined __s390__
   1.159 +#define RETURN_INSTR 0x07fe0000 /* br %r14 */
   1.160 +
   1.161 +#elif defined __aarch64__
   1.162 +#define RETURN_INSTR 0xd65f03c0 /* ret */
   1.163 +
   1.164 +#elif defined __ia64
   1.165 +struct ia64_instr { uint32_t i[4]; };
   1.166 +static const ia64_instr _return_instr =
   1.167 +  {{ 0x00000011, 0x00000001, 0x80000200, 0x00840008 }}; /* br.ret.sptk.many b0 */
   1.168 +
   1.169 +#define RETURN_INSTR _return_instr
   1.170 +#define RETURN_INSTR_TYPE ia64_instr
   1.171 +
   1.172 +#else
   1.173 +#error "Need return instruction for this architecture"
   1.174 +#endif
   1.175 +
   1.176 +#ifndef RETURN_INSTR_TYPE
   1.177 +#define RETURN_INSTR_TYPE uint32_t
   1.178 +#endif
   1.179 +
   1.180 +// Miscellaneous Windows/Unix portability gumph
   1.181 +
   1.182 +#ifdef _WIN32
   1.183 +// Uses of this function deliberately leak the string.
   1.184 +static LPSTR
   1.185 +StrW32Error(DWORD errcode)
   1.186 +{
   1.187 +  LPSTR errmsg;
   1.188 +  FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
   1.189 +                 FORMAT_MESSAGE_FROM_SYSTEM |
   1.190 +                 FORMAT_MESSAGE_IGNORE_INSERTS,
   1.191 +                 nullptr, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
   1.192 +                 (LPSTR)&errmsg, 0, nullptr);
   1.193 +
   1.194 +  // FormatMessage puts an unwanted newline at the end of the string
   1.195 +  size_t n = strlen(errmsg)-1;
   1.196 +  while (errmsg[n] == '\r' || errmsg[n] == '\n') n--;
   1.197 +  errmsg[n+1] = '\0';
   1.198 +  return errmsg;
   1.199 +}
   1.200 +#define LastErrMsg() (StrW32Error(GetLastError()))
   1.201 +
   1.202 +// Because we use VirtualAlloc in MEM_RESERVE mode, the "page size" we want
   1.203 +// is the allocation granularity.
   1.204 +static SYSTEM_INFO _sinfo;
   1.205 +#undef PAGESIZE
   1.206 +#define PAGESIZE (_sinfo.dwAllocationGranularity)
   1.207 +
   1.208 +
   1.209 +static void *
   1.210 +ReserveRegion(uintptr_t request, bool accessible)
   1.211 +{
   1.212 +  return VirtualAlloc((void *)request, PAGESIZE,
   1.213 +                      accessible ? MEM_RESERVE|MEM_COMMIT : MEM_RESERVE,
   1.214 +                      accessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS);
   1.215 +}
   1.216 +
   1.217 +static void
   1.218 +ReleaseRegion(void *page)
   1.219 +{
   1.220 +  VirtualFree(page, PAGESIZE, MEM_RELEASE);
   1.221 +}
   1.222 +
   1.223 +static bool
   1.224 +ProbeRegion(uintptr_t page)
   1.225 +{
   1.226 +  if (page >= (uintptr_t)_sinfo.lpMaximumApplicationAddress &&
   1.227 +      page + PAGESIZE >= (uintptr_t)_sinfo.lpMaximumApplicationAddress) {
   1.228 +    return true;
   1.229 +  } else {
   1.230 +    return false;
   1.231 +  }
   1.232 +}
   1.233 +
   1.234 +static bool
   1.235 +MakeRegionExecutable(void *)
   1.236 +{
   1.237 +  return false;
   1.238 +}
   1.239 +
   1.240 +#undef MAP_FAILED
   1.241 +#define MAP_FAILED 0
   1.242 +
   1.243 +#else // Unix
   1.244 +
   1.245 +#define LastErrMsg() (strerror(errno))
   1.246 +
   1.247 +static unsigned long _pagesize;
   1.248 +#define PAGESIZE _pagesize
   1.249 +
   1.250 +static void *
   1.251 +ReserveRegion(uintptr_t request, bool accessible)
   1.252 +{
   1.253 +  return mmap(reinterpret_cast<void*>(request), PAGESIZE,
   1.254 +              accessible ? PROT_READ|PROT_WRITE : PROT_NONE,
   1.255 +              MAP_PRIVATE|MAP_ANON, -1, 0);
   1.256 +}
   1.257 +
   1.258 +static void
   1.259 +ReleaseRegion(void *page)
   1.260 +{
   1.261 +  munmap(page, PAGESIZE);
   1.262 +}
   1.263 +
   1.264 +static bool
   1.265 +ProbeRegion(uintptr_t page)
   1.266 +{
   1.267 +  if (madvise(reinterpret_cast<void*>(page), PAGESIZE, MADV_NORMAL)) {
   1.268 +    return true;
   1.269 +  } else {
   1.270 +    return false;
   1.271 +  }
   1.272 +}
   1.273 +
   1.274 +static int
   1.275 +MakeRegionExecutable(void *page)
   1.276 +{
   1.277 +  return mprotect((caddr_t)page, PAGESIZE, PROT_READ|PROT_WRITE|PROT_EXEC);
   1.278 +}
   1.279 +
   1.280 +#endif
   1.281 +
   1.282 +static uintptr_t
   1.283 +ReservePoisonArea()
   1.284 +{
   1.285 +  if (sizeof(uintptr_t) == 8) {
   1.286 +    // Use the hardware-inaccessible region.
   1.287 +    // We have to avoid 64-bit constants and shifts by 32 bits, since this
   1.288 +    // code is compiled in 32-bit mode, although it is never executed there.
   1.289 +    uintptr_t result = (((uintptr_t(0x7FFFFFFFu) << 31) << 1 |
   1.290 +                         uintptr_t(0xF0DEAFFFu)) &
   1.291 +                        ~uintptr_t(PAGESIZE-1));
   1.292 +    printf("INFO | poison area assumed at 0x%.*" PRIxPTR "\n", SIZxPTR, result);
   1.293 +    return result;
   1.294 +  } else {
   1.295 +    // First see if we can allocate the preferred poison address from the OS.
   1.296 +    uintptr_t candidate = (0xF0DEAFFF & ~(PAGESIZE-1));
   1.297 +    void *result = ReserveRegion(candidate, false);
   1.298 +    if (result == (void *)candidate) {
   1.299 +      // success - inaccessible page allocated
   1.300 +      printf("INFO | poison area allocated at 0x%.*" PRIxPTR
   1.301 +             " (preferred addr)\n", SIZxPTR, (uintptr_t)result);
   1.302 +      return candidate;
   1.303 +    }
   1.304 +
   1.305 +    // That didn't work, so see if the preferred address is within a range
   1.306 +    // of permanently inacessible memory.
   1.307 +    if (ProbeRegion(candidate)) {
   1.308 +      // success - selected page cannot be usable memory
   1.309 +      if (result != MAP_FAILED)
   1.310 +        ReleaseRegion(result);
   1.311 +      printf("INFO | poison area assumed at 0x%.*" PRIxPTR
   1.312 +             " (preferred addr)\n", SIZxPTR, candidate);
   1.313 +      return candidate;
   1.314 +    }
   1.315 +
   1.316 +    // The preferred address is already in use.  Did the OS give us a
   1.317 +    // consolation prize?
   1.318 +    if (result != MAP_FAILED) {
   1.319 +      printf("INFO | poison area allocated at 0x%.*" PRIxPTR
   1.320 +             " (consolation prize)\n", SIZxPTR, (uintptr_t)result);
   1.321 +      return (uintptr_t)result;
   1.322 +    }
   1.323 +
   1.324 +    // It didn't, so try to allocate again, without any constraint on
   1.325 +    // the address.
   1.326 +    result = ReserveRegion(0, false);
   1.327 +    if (result != MAP_FAILED) {
   1.328 +      printf("INFO | poison area allocated at 0x%.*" PRIxPTR
   1.329 +             " (fallback)\n", SIZxPTR, (uintptr_t)result);
   1.330 +      return (uintptr_t)result;
   1.331 +    }
   1.332 +
   1.333 +    printf("ERROR | no usable poison area found\n");
   1.334 +    return 0;
   1.335 +  }
   1.336 +}
   1.337 +
   1.338 +/* The "positive control" area confirms that we can allocate a page with the
   1.339 + * proper characteristics.
   1.340 + */
   1.341 +static uintptr_t
   1.342 +ReservePositiveControl()
   1.343 +{
   1.344 +
   1.345 +  void *result = ReserveRegion(0, false);
   1.346 +  if (result == MAP_FAILED) {
   1.347 +    printf("ERROR | allocating positive control | %s\n", LastErrMsg());
   1.348 +    return 0;
   1.349 +  }
   1.350 +  printf("INFO | positive control allocated at 0x%.*" PRIxPTR "\n",
   1.351 +         SIZxPTR, (uintptr_t)result);
   1.352 +  return (uintptr_t)result;
   1.353 +}
   1.354 +
   1.355 +/* The "negative control" area confirms that our probe logic does detect a
   1.356 + * page that is readable, writable, or executable.
   1.357 + */
   1.358 +static uintptr_t
   1.359 +ReserveNegativeControl()
   1.360 +{
   1.361 +  void *result = ReserveRegion(0, true);
   1.362 +  if (result == MAP_FAILED) {
   1.363 +    printf("ERROR | allocating negative control | %s\n", LastErrMsg());
   1.364 +    return 0;
   1.365 +  }
   1.366 +
   1.367 +  // Fill the page with return instructions.
   1.368 +  RETURN_INSTR_TYPE *p = (RETURN_INSTR_TYPE *)result;
   1.369 +  RETURN_INSTR_TYPE *limit = (RETURN_INSTR_TYPE *)(((char *)result) + PAGESIZE);
   1.370 +  while (p < limit)
   1.371 +    *p++ = RETURN_INSTR;
   1.372 +
   1.373 +  // Now mark it executable as well as readable and writable.
   1.374 +  // (mmap(PROT_EXEC) may fail when applied to anonymous memory.)
   1.375 +
   1.376 +  if (MakeRegionExecutable(result)) {
   1.377 +    printf("ERROR | making negative control executable | %s\n", LastErrMsg());
   1.378 +    return 0;
   1.379 +  }
   1.380 +
   1.381 +  printf("INFO | negative control allocated at 0x%.*" PRIxPTR "\n",
   1.382 +         SIZxPTR, (uintptr_t)result);
   1.383 +  return (uintptr_t)result;
   1.384 +}
   1.385 +
   1.386 +static void
   1.387 +JumpTo(uintptr_t opaddr)
   1.388 +{
   1.389 +#ifdef __ia64
   1.390 +  struct func_call {
   1.391 +    uintptr_t func;
   1.392 +    uintptr_t gp;
   1.393 +  } call = { opaddr, };
   1.394 +  ((void (*)())&call)();
   1.395 +#else
   1.396 +  ((void (*)())opaddr)();
   1.397 +#endif
   1.398 +}
   1.399 +
   1.400 +#ifdef _WIN32
   1.401 +static BOOL
   1.402 +IsBadExecPtr(uintptr_t ptr)
   1.403 +{
   1.404 +  BOOL ret = false;
   1.405 +
   1.406 +#ifdef _MSC_VER
   1.407 +  __try {
   1.408 +    JumpTo(ptr);
   1.409 +  } __except (EXCEPTION_EXECUTE_HANDLER) {
   1.410 +    ret = true;
   1.411 +  }
   1.412 +#else
   1.413 +  printf("INFO | exec test not supported on MinGW build\n");
   1.414 +  // We do our best
   1.415 +  ret = IsBadReadPtr((const void*)ptr, 1);
   1.416 +#endif
   1.417 +  return ret;
   1.418 +}
   1.419 +#endif
   1.420 +
   1.421 +/* Test each page.  */
   1.422 +static bool
   1.423 +TestPage(const char *pagelabel, uintptr_t pageaddr, int should_succeed)
   1.424 +{
   1.425 +  const char *oplabel;
   1.426 +  uintptr_t opaddr;
   1.427 +
   1.428 +  bool failed = false;
   1.429 +  for (unsigned int test = 0; test < 3; test++) {
   1.430 +    switch (test) {
   1.431 +      // The execute test must be done before the write test, because the
   1.432 +      // write test will clobber memory at the target address.
   1.433 +    case 0: oplabel = "reading"; opaddr = pageaddr + PAGESIZE/2 - 1; break;
   1.434 +    case 1: oplabel = "executing"; opaddr = pageaddr + PAGESIZE/2; break;
   1.435 +    case 2: oplabel = "writing"; opaddr = pageaddr + PAGESIZE/2 - 1; break;
   1.436 +    default: abort();
   1.437 +    }
   1.438 +
   1.439 +#ifdef _WIN32
   1.440 +    BOOL badptr;
   1.441 +
   1.442 +    switch (test) {
   1.443 +    case 0: badptr = IsBadReadPtr((const void*)opaddr, 1); break;
   1.444 +    case 1: badptr = IsBadExecPtr(opaddr); break;
   1.445 +    case 2: badptr = IsBadWritePtr((void*)opaddr, 1); break;
   1.446 +    default: abort();
   1.447 +    }
   1.448 +
   1.449 +    if (badptr) {
   1.450 +      if (should_succeed) {
   1.451 +        printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, pagelabel);
   1.452 +        failed = true;
   1.453 +      } else {
   1.454 +        printf("TEST-PASS | %s %s\n", oplabel, pagelabel);
   1.455 +      }
   1.456 +    } else {
   1.457 +      // if control reaches this point the probe succeeded
   1.458 +      if (should_succeed) {
   1.459 +        printf("TEST-PASS | %s %s\n", oplabel, pagelabel);
   1.460 +      } else {
   1.461 +        printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, pagelabel);
   1.462 +        failed = true;
   1.463 +      }
   1.464 +    }
   1.465 +#else
   1.466 +    pid_t pid = fork();
   1.467 +    if (pid == -1) {
   1.468 +      printf("ERROR | %s %s | fork=%s\n", oplabel, pagelabel,
   1.469 +             LastErrMsg());
   1.470 +      exit(2);
   1.471 +    } else if (pid == 0) {
   1.472 +      volatile unsigned char scratch;
   1.473 +      switch (test) {
   1.474 +      case 0: scratch = *(volatile unsigned char *)opaddr; break;
   1.475 +      case 1: JumpTo(opaddr); break;
   1.476 +      case 2: *(volatile unsigned char *)opaddr = 0; break;
   1.477 +      default: abort();
   1.478 +      }
   1.479 +      (void)scratch;
   1.480 +      _exit(0);
   1.481 +    } else {
   1.482 +      int status;
   1.483 +      if (waitpid(pid, &status, 0) != pid) {
   1.484 +        printf("ERROR | %s %s | wait=%s\n", oplabel, pagelabel,
   1.485 +               LastErrMsg());
   1.486 +        exit(2);
   1.487 +      }
   1.488 +
   1.489 +      if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
   1.490 +        if (should_succeed) {
   1.491 +          printf("TEST-PASS | %s %s\n", oplabel, pagelabel);
   1.492 +        } else {
   1.493 +          printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected successful exit\n",
   1.494 +                 oplabel, pagelabel);
   1.495 +          failed = true;
   1.496 +        }
   1.497 +      } else if (WIFEXITED(status)) {
   1.498 +        printf("ERROR | %s %s | unexpected exit code %d\n",
   1.499 +               oplabel, pagelabel, WEXITSTATUS(status));
   1.500 +        exit(2);
   1.501 +      } else if (WIFSIGNALED(status)) {
   1.502 +        if (should_succeed) {
   1.503 +          printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected signal %d\n",
   1.504 +                 oplabel, pagelabel, WTERMSIG(status));
   1.505 +          failed = true;
   1.506 +        } else {
   1.507 +          printf("TEST-PASS | %s %s | signal %d (as expected)\n",
   1.508 +                 oplabel, pagelabel, WTERMSIG(status));
   1.509 +        }
   1.510 +      } else {
   1.511 +        printf("ERROR | %s %s | unexpected exit status %d\n",
   1.512 +               oplabel, pagelabel, status);
   1.513 +        exit(2);
   1.514 +      }
   1.515 +    }
   1.516 +#endif
   1.517 +  }
   1.518 +  return failed;
   1.519 +}
   1.520 +
   1.521 +int
   1.522 +main()
   1.523 +{
   1.524 +#ifdef _WIN32
   1.525 +  GetSystemInfo(&_sinfo);
   1.526 +#else
   1.527 +  _pagesize = sysconf(_SC_PAGESIZE);
   1.528 +#endif
   1.529 +
   1.530 +  uintptr_t ncontrol = ReserveNegativeControl();
   1.531 +  uintptr_t pcontrol = ReservePositiveControl();
   1.532 +  uintptr_t poison = ReservePoisonArea();
   1.533 +
   1.534 +  if (!ncontrol || !pcontrol || !poison)
   1.535 +    return 2;
   1.536 +
   1.537 +  bool failed = false;
   1.538 +  failed |= TestPage("negative control", ncontrol, 1);
   1.539 +  failed |= TestPage("positive control", pcontrol, 0);
   1.540 +  failed |= TestPage("poison area", poison, 0);
   1.541 +
   1.542 +  return failed ? 1 : 0;
   1.543 +}

mercurial