mfbt/tests/TestPoisonArea.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 6 */
michael@0 7
michael@0 8 /* Code in this file needs to be kept in sync with code in nsPresArena.cpp.
michael@0 9 *
michael@0 10 * We want to use a fixed address for frame poisoning so that it is readily
michael@0 11 * identifiable in crash dumps. Whether such an address is available
michael@0 12 * without any special setup depends on the system configuration.
michael@0 13 *
michael@0 14 * All current 64-bit CPUs (with the possible exception of PowerPC64)
michael@0 15 * reserve the vast majority of the virtual address space for future
michael@0 16 * hardware extensions; valid addresses must be below some break point
michael@0 17 * between 2**48 and 2**54, depending on exactly which chip you have. Some
michael@0 18 * chips (notably amd64) also allow the use of the *highest* 2**48 -- 2**54
michael@0 19 * addresses. Thus, if user space pointers are 64 bits wide, we can just
michael@0 20 * use an address outside this range, and no more is required. To
michael@0 21 * accommodate the chips that allow very high addresses to be valid, the
michael@0 22 * value chosen is close to 2**63 (that is, in the middle of the space).
michael@0 23 *
michael@0 24 * In most cases, a purely 32-bit operating system must reserve some
michael@0 25 * fraction of the address space for its own use. Contemporary 32-bit OSes
michael@0 26 * tend to take the high gigabyte or so (0xC000_0000 on up). If we can
michael@0 27 * prove that high addresses are reserved to the kernel, we can use an
michael@0 28 * address in that region. Unfortunately, not all 32-bit OSes do this;
michael@0 29 * OSX 10.4 might not, and it is unclear what mobile OSes are like
michael@0 30 * (some 32-bit CPUs make it very easy for the kernel to exist in its own
michael@0 31 * private address space).
michael@0 32 *
michael@0 33 * Furthermore, when a 32-bit user space process is running on a 64-bit
michael@0 34 * kernel, the operating system has no need to reserve any of the space that
michael@0 35 * the process can see, and generally does not do so. This is the scenario
michael@0 36 * of greatest concern, since it covers all contemporary OSX iterations
michael@0 37 * (10.5+) as well as Windows Vista and 7 on newer amd64 hardware. Linux on
michael@0 38 * amd64 is generally run as a pure 64-bit environment, but its 32-bit
michael@0 39 * compatibility mode also has this property.
michael@0 40 *
michael@0 41 * Thus, when user space pointers are 32 bits wide, we need to validate
michael@0 42 * our chosen address, and possibly *make* it a good poison address by
michael@0 43 * allocating a page around it and marking it inaccessible. The algorithm
michael@0 44 * for this is:
michael@0 45 *
michael@0 46 * 1. Attempt to make the page surrounding the poison address a reserved,
michael@0 47 * inaccessible memory region using OS primitives. On Windows, this is
michael@0 48 * done with VirtualAlloc(MEM_RESERVE); on Unix, mmap(PROT_NONE).
michael@0 49 *
michael@0 50 * 2. If mmap/VirtualAlloc failed, there are two possible reasons: either
michael@0 51 * the region is reserved to the kernel and no further action is
michael@0 52 * required, or there is already usable memory in this area and we have
michael@0 53 * to pick a different address. The tricky part is knowing which case
michael@0 54 * we have, without attempting to access the region. On Windows, we
michael@0 55 * rely on GetSystemInfo()'s reported upper and lower bounds of the
michael@0 56 * application memory area. On Unix, there is nothing devoted to the
michael@0 57 * purpose, but seeing if madvise() fails is close enough (it *might*
michael@0 58 * disrupt someone else's use of the memory region, but not by as much
michael@0 59 * as anything else available).
michael@0 60 *
michael@0 61 * Be aware of these gotchas:
michael@0 62 *
michael@0 63 * 1. We cannot use mmap() with MAP_FIXED. MAP_FIXED is defined to
michael@0 64 * _replace_ any existing mapping in the region, if necessary to satisfy
michael@0 65 * the request. Obviously, as we are blindly attempting to acquire a
michael@0 66 * page at a constant address, we must not do this, lest we overwrite
michael@0 67 * someone else's allocation.
michael@0 68 *
michael@0 69 * 2. For the same reason, we cannot blindly use mprotect() if mmap() fails.
michael@0 70 *
michael@0 71 * 3. madvise() may fail when applied to a 'magic' memory region provided as
michael@0 72 * a kernel/user interface. Fortunately, the only such case I know about
michael@0 73 * is the "vsyscall" area (not to be confused with the "vdso" area) for
michael@0 74 * *64*-bit processes on Linux - and we don't even run this code for
michael@0 75 * 64-bit processes.
michael@0 76 *
michael@0 77 * 4. VirtualQuery() does not produce any useful information if
michael@0 78 * applied to kernel memory - in fact, it doesn't write its output
michael@0 79 * at all. Thus, it is not used here.
michael@0 80 */
michael@0 81
michael@0 82 #include "mozilla/IntegerPrintfMacros.h"
michael@0 83 #include "mozilla/NullPtr.h"
michael@0 84
michael@0 85 // MAP_ANON(YMOUS) is not in any standard. Add defines as necessary.
michael@0 86 #define _GNU_SOURCE 1
michael@0 87 #define _DARWIN_C_SOURCE 1
michael@0 88
michael@0 89 #include <stddef.h>
michael@0 90
michael@0 91 #include <errno.h>
michael@0 92 #include <stdio.h>
michael@0 93 #include <stdlib.h>
michael@0 94 #include <string.h>
michael@0 95
michael@0 96 #ifdef _WIN32
michael@0 97 #include <windows.h>
michael@0 98 #else
michael@0 99 #include <sys/types.h>
michael@0 100 #include <fcntl.h>
michael@0 101 #include <signal.h>
michael@0 102 #include <unistd.h>
michael@0 103 #include <sys/stat.h>
michael@0 104 #include <sys/wait.h>
michael@0 105
michael@0 106 #include <sys/mman.h>
michael@0 107 #ifndef MAP_ANON
michael@0 108 #ifdef MAP_ANONYMOUS
michael@0 109 #define MAP_ANON MAP_ANONYMOUS
michael@0 110 #else
michael@0 111 #error "Don't know how to get anonymous memory"
michael@0 112 #endif
michael@0 113 #endif
michael@0 114 #endif
michael@0 115
michael@0 116 #define SIZxPTR ((int)(sizeof(uintptr_t)*2))
michael@0 117
michael@0 118 /* This program assumes that a whole number of return instructions fit into
michael@0 119 * 32 bits, and that 32-bit alignment is sufficient for a branch destination.
michael@0 120 * For architectures where this is not true, fiddling with RETURN_INSTR_TYPE
michael@0 121 * can be enough.
michael@0 122 */
michael@0 123
michael@0 124 #if defined __i386__ || defined __x86_64__ || \
michael@0 125 defined __i386 || defined __x86_64 || \
michael@0 126 defined _M_IX86 || defined _M_AMD64
michael@0 127 #define RETURN_INSTR 0xC3C3C3C3 /* ret; ret; ret; ret */
michael@0 128
michael@0 129 #elif defined __arm__ || defined _M_ARM
michael@0 130 #define RETURN_INSTR 0xE12FFF1E /* bx lr */
michael@0 131
michael@0 132 // PPC has its own style of CPU-id #defines. There is no Windows for
michael@0 133 // PPC as far as I know, so no _M_ variant.
michael@0 134 #elif defined _ARCH_PPC || defined _ARCH_PWR || defined _ARCH_PWR2
michael@0 135 #define RETURN_INSTR 0x4E800020 /* blr */
michael@0 136
michael@0 137 #elif defined __sparc || defined __sparcv9
michael@0 138 #define RETURN_INSTR 0x81c3e008 /* retl */
michael@0 139
michael@0 140 #elif defined __alpha
michael@0 141 #define RETURN_INSTR 0x6bfa8001 /* ret */
michael@0 142
michael@0 143 #elif defined __hppa
michael@0 144 #define RETURN_INSTR 0xe840c002 /* bv,n r0(rp) */
michael@0 145
michael@0 146 #elif defined __mips
michael@0 147 #define RETURN_INSTR 0x03e00008 /* jr ra */
michael@0 148
michael@0 149 #ifdef __MIPSEL
michael@0 150 /* On mipsel, jr ra needs to be followed by a nop.
michael@0 151 0x03e00008 as a 64 bits integer just does that */
michael@0 152 #define RETURN_INSTR_TYPE uint64_t
michael@0 153 #endif
michael@0 154
michael@0 155 #elif defined __s390__
michael@0 156 #define RETURN_INSTR 0x07fe0000 /* br %r14 */
michael@0 157
michael@0 158 #elif defined __aarch64__
michael@0 159 #define RETURN_INSTR 0xd65f03c0 /* ret */
michael@0 160
michael@0 161 #elif defined __ia64
michael@0 162 struct ia64_instr { uint32_t i[4]; };
michael@0 163 static const ia64_instr _return_instr =
michael@0 164 {{ 0x00000011, 0x00000001, 0x80000200, 0x00840008 }}; /* br.ret.sptk.many b0 */
michael@0 165
michael@0 166 #define RETURN_INSTR _return_instr
michael@0 167 #define RETURN_INSTR_TYPE ia64_instr
michael@0 168
michael@0 169 #else
michael@0 170 #error "Need return instruction for this architecture"
michael@0 171 #endif
michael@0 172
michael@0 173 #ifndef RETURN_INSTR_TYPE
michael@0 174 #define RETURN_INSTR_TYPE uint32_t
michael@0 175 #endif
michael@0 176
michael@0 177 // Miscellaneous Windows/Unix portability gumph
michael@0 178
michael@0 179 #ifdef _WIN32
michael@0 180 // Uses of this function deliberately leak the string.
michael@0 181 static LPSTR
michael@0 182 StrW32Error(DWORD errcode)
michael@0 183 {
michael@0 184 LPSTR errmsg;
michael@0 185 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
michael@0 186 FORMAT_MESSAGE_FROM_SYSTEM |
michael@0 187 FORMAT_MESSAGE_IGNORE_INSERTS,
michael@0 188 nullptr, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
michael@0 189 (LPSTR)&errmsg, 0, nullptr);
michael@0 190
michael@0 191 // FormatMessage puts an unwanted newline at the end of the string
michael@0 192 size_t n = strlen(errmsg)-1;
michael@0 193 while (errmsg[n] == '\r' || errmsg[n] == '\n') n--;
michael@0 194 errmsg[n+1] = '\0';
michael@0 195 return errmsg;
michael@0 196 }
michael@0 197 #define LastErrMsg() (StrW32Error(GetLastError()))
michael@0 198
michael@0 199 // Because we use VirtualAlloc in MEM_RESERVE mode, the "page size" we want
michael@0 200 // is the allocation granularity.
michael@0 201 static SYSTEM_INFO _sinfo;
michael@0 202 #undef PAGESIZE
michael@0 203 #define PAGESIZE (_sinfo.dwAllocationGranularity)
michael@0 204
michael@0 205
michael@0 206 static void *
michael@0 207 ReserveRegion(uintptr_t request, bool accessible)
michael@0 208 {
michael@0 209 return VirtualAlloc((void *)request, PAGESIZE,
michael@0 210 accessible ? MEM_RESERVE|MEM_COMMIT : MEM_RESERVE,
michael@0 211 accessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS);
michael@0 212 }
michael@0 213
michael@0 214 static void
michael@0 215 ReleaseRegion(void *page)
michael@0 216 {
michael@0 217 VirtualFree(page, PAGESIZE, MEM_RELEASE);
michael@0 218 }
michael@0 219
michael@0 220 static bool
michael@0 221 ProbeRegion(uintptr_t page)
michael@0 222 {
michael@0 223 if (page >= (uintptr_t)_sinfo.lpMaximumApplicationAddress &&
michael@0 224 page + PAGESIZE >= (uintptr_t)_sinfo.lpMaximumApplicationAddress) {
michael@0 225 return true;
michael@0 226 } else {
michael@0 227 return false;
michael@0 228 }
michael@0 229 }
michael@0 230
michael@0 231 static bool
michael@0 232 MakeRegionExecutable(void *)
michael@0 233 {
michael@0 234 return false;
michael@0 235 }
michael@0 236
michael@0 237 #undef MAP_FAILED
michael@0 238 #define MAP_FAILED 0
michael@0 239
michael@0 240 #else // Unix
michael@0 241
michael@0 242 #define LastErrMsg() (strerror(errno))
michael@0 243
michael@0 244 static unsigned long _pagesize;
michael@0 245 #define PAGESIZE _pagesize
michael@0 246
michael@0 247 static void *
michael@0 248 ReserveRegion(uintptr_t request, bool accessible)
michael@0 249 {
michael@0 250 return mmap(reinterpret_cast<void*>(request), PAGESIZE,
michael@0 251 accessible ? PROT_READ|PROT_WRITE : PROT_NONE,
michael@0 252 MAP_PRIVATE|MAP_ANON, -1, 0);
michael@0 253 }
michael@0 254
michael@0 255 static void
michael@0 256 ReleaseRegion(void *page)
michael@0 257 {
michael@0 258 munmap(page, PAGESIZE);
michael@0 259 }
michael@0 260
michael@0 261 static bool
michael@0 262 ProbeRegion(uintptr_t page)
michael@0 263 {
michael@0 264 if (madvise(reinterpret_cast<void*>(page), PAGESIZE, MADV_NORMAL)) {
michael@0 265 return true;
michael@0 266 } else {
michael@0 267 return false;
michael@0 268 }
michael@0 269 }
michael@0 270
michael@0 271 static int
michael@0 272 MakeRegionExecutable(void *page)
michael@0 273 {
michael@0 274 return mprotect((caddr_t)page, PAGESIZE, PROT_READ|PROT_WRITE|PROT_EXEC);
michael@0 275 }
michael@0 276
michael@0 277 #endif
michael@0 278
michael@0 279 static uintptr_t
michael@0 280 ReservePoisonArea()
michael@0 281 {
michael@0 282 if (sizeof(uintptr_t) == 8) {
michael@0 283 // Use the hardware-inaccessible region.
michael@0 284 // We have to avoid 64-bit constants and shifts by 32 bits, since this
michael@0 285 // code is compiled in 32-bit mode, although it is never executed there.
michael@0 286 uintptr_t result = (((uintptr_t(0x7FFFFFFFu) << 31) << 1 |
michael@0 287 uintptr_t(0xF0DEAFFFu)) &
michael@0 288 ~uintptr_t(PAGESIZE-1));
michael@0 289 printf("INFO | poison area assumed at 0x%.*" PRIxPTR "\n", SIZxPTR, result);
michael@0 290 return result;
michael@0 291 } else {
michael@0 292 // First see if we can allocate the preferred poison address from the OS.
michael@0 293 uintptr_t candidate = (0xF0DEAFFF & ~(PAGESIZE-1));
michael@0 294 void *result = ReserveRegion(candidate, false);
michael@0 295 if (result == (void *)candidate) {
michael@0 296 // success - inaccessible page allocated
michael@0 297 printf("INFO | poison area allocated at 0x%.*" PRIxPTR
michael@0 298 " (preferred addr)\n", SIZxPTR, (uintptr_t)result);
michael@0 299 return candidate;
michael@0 300 }
michael@0 301
michael@0 302 // That didn't work, so see if the preferred address is within a range
michael@0 303 // of permanently inacessible memory.
michael@0 304 if (ProbeRegion(candidate)) {
michael@0 305 // success - selected page cannot be usable memory
michael@0 306 if (result != MAP_FAILED)
michael@0 307 ReleaseRegion(result);
michael@0 308 printf("INFO | poison area assumed at 0x%.*" PRIxPTR
michael@0 309 " (preferred addr)\n", SIZxPTR, candidate);
michael@0 310 return candidate;
michael@0 311 }
michael@0 312
michael@0 313 // The preferred address is already in use. Did the OS give us a
michael@0 314 // consolation prize?
michael@0 315 if (result != MAP_FAILED) {
michael@0 316 printf("INFO | poison area allocated at 0x%.*" PRIxPTR
michael@0 317 " (consolation prize)\n", SIZxPTR, (uintptr_t)result);
michael@0 318 return (uintptr_t)result;
michael@0 319 }
michael@0 320
michael@0 321 // It didn't, so try to allocate again, without any constraint on
michael@0 322 // the address.
michael@0 323 result = ReserveRegion(0, false);
michael@0 324 if (result != MAP_FAILED) {
michael@0 325 printf("INFO | poison area allocated at 0x%.*" PRIxPTR
michael@0 326 " (fallback)\n", SIZxPTR, (uintptr_t)result);
michael@0 327 return (uintptr_t)result;
michael@0 328 }
michael@0 329
michael@0 330 printf("ERROR | no usable poison area found\n");
michael@0 331 return 0;
michael@0 332 }
michael@0 333 }
michael@0 334
michael@0 335 /* The "positive control" area confirms that we can allocate a page with the
michael@0 336 * proper characteristics.
michael@0 337 */
michael@0 338 static uintptr_t
michael@0 339 ReservePositiveControl()
michael@0 340 {
michael@0 341
michael@0 342 void *result = ReserveRegion(0, false);
michael@0 343 if (result == MAP_FAILED) {
michael@0 344 printf("ERROR | allocating positive control | %s\n", LastErrMsg());
michael@0 345 return 0;
michael@0 346 }
michael@0 347 printf("INFO | positive control allocated at 0x%.*" PRIxPTR "\n",
michael@0 348 SIZxPTR, (uintptr_t)result);
michael@0 349 return (uintptr_t)result;
michael@0 350 }
michael@0 351
michael@0 352 /* The "negative control" area confirms that our probe logic does detect a
michael@0 353 * page that is readable, writable, or executable.
michael@0 354 */
michael@0 355 static uintptr_t
michael@0 356 ReserveNegativeControl()
michael@0 357 {
michael@0 358 void *result = ReserveRegion(0, true);
michael@0 359 if (result == MAP_FAILED) {
michael@0 360 printf("ERROR | allocating negative control | %s\n", LastErrMsg());
michael@0 361 return 0;
michael@0 362 }
michael@0 363
michael@0 364 // Fill the page with return instructions.
michael@0 365 RETURN_INSTR_TYPE *p = (RETURN_INSTR_TYPE *)result;
michael@0 366 RETURN_INSTR_TYPE *limit = (RETURN_INSTR_TYPE *)(((char *)result) + PAGESIZE);
michael@0 367 while (p < limit)
michael@0 368 *p++ = RETURN_INSTR;
michael@0 369
michael@0 370 // Now mark it executable as well as readable and writable.
michael@0 371 // (mmap(PROT_EXEC) may fail when applied to anonymous memory.)
michael@0 372
michael@0 373 if (MakeRegionExecutable(result)) {
michael@0 374 printf("ERROR | making negative control executable | %s\n", LastErrMsg());
michael@0 375 return 0;
michael@0 376 }
michael@0 377
michael@0 378 printf("INFO | negative control allocated at 0x%.*" PRIxPTR "\n",
michael@0 379 SIZxPTR, (uintptr_t)result);
michael@0 380 return (uintptr_t)result;
michael@0 381 }
michael@0 382
michael@0 383 static void
michael@0 384 JumpTo(uintptr_t opaddr)
michael@0 385 {
michael@0 386 #ifdef __ia64
michael@0 387 struct func_call {
michael@0 388 uintptr_t func;
michael@0 389 uintptr_t gp;
michael@0 390 } call = { opaddr, };
michael@0 391 ((void (*)())&call)();
michael@0 392 #else
michael@0 393 ((void (*)())opaddr)();
michael@0 394 #endif
michael@0 395 }
michael@0 396
michael@0 397 #ifdef _WIN32
michael@0 398 static BOOL
michael@0 399 IsBadExecPtr(uintptr_t ptr)
michael@0 400 {
michael@0 401 BOOL ret = false;
michael@0 402
michael@0 403 #ifdef _MSC_VER
michael@0 404 __try {
michael@0 405 JumpTo(ptr);
michael@0 406 } __except (EXCEPTION_EXECUTE_HANDLER) {
michael@0 407 ret = true;
michael@0 408 }
michael@0 409 #else
michael@0 410 printf("INFO | exec test not supported on MinGW build\n");
michael@0 411 // We do our best
michael@0 412 ret = IsBadReadPtr((const void*)ptr, 1);
michael@0 413 #endif
michael@0 414 return ret;
michael@0 415 }
michael@0 416 #endif
michael@0 417
michael@0 418 /* Test each page. */
michael@0 419 static bool
michael@0 420 TestPage(const char *pagelabel, uintptr_t pageaddr, int should_succeed)
michael@0 421 {
michael@0 422 const char *oplabel;
michael@0 423 uintptr_t opaddr;
michael@0 424
michael@0 425 bool failed = false;
michael@0 426 for (unsigned int test = 0; test < 3; test++) {
michael@0 427 switch (test) {
michael@0 428 // The execute test must be done before the write test, because the
michael@0 429 // write test will clobber memory at the target address.
michael@0 430 case 0: oplabel = "reading"; opaddr = pageaddr + PAGESIZE/2 - 1; break;
michael@0 431 case 1: oplabel = "executing"; opaddr = pageaddr + PAGESIZE/2; break;
michael@0 432 case 2: oplabel = "writing"; opaddr = pageaddr + PAGESIZE/2 - 1; break;
michael@0 433 default: abort();
michael@0 434 }
michael@0 435
michael@0 436 #ifdef _WIN32
michael@0 437 BOOL badptr;
michael@0 438
michael@0 439 switch (test) {
michael@0 440 case 0: badptr = IsBadReadPtr((const void*)opaddr, 1); break;
michael@0 441 case 1: badptr = IsBadExecPtr(opaddr); break;
michael@0 442 case 2: badptr = IsBadWritePtr((void*)opaddr, 1); break;
michael@0 443 default: abort();
michael@0 444 }
michael@0 445
michael@0 446 if (badptr) {
michael@0 447 if (should_succeed) {
michael@0 448 printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, pagelabel);
michael@0 449 failed = true;
michael@0 450 } else {
michael@0 451 printf("TEST-PASS | %s %s\n", oplabel, pagelabel);
michael@0 452 }
michael@0 453 } else {
michael@0 454 // if control reaches this point the probe succeeded
michael@0 455 if (should_succeed) {
michael@0 456 printf("TEST-PASS | %s %s\n", oplabel, pagelabel);
michael@0 457 } else {
michael@0 458 printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, pagelabel);
michael@0 459 failed = true;
michael@0 460 }
michael@0 461 }
michael@0 462 #else
michael@0 463 pid_t pid = fork();
michael@0 464 if (pid == -1) {
michael@0 465 printf("ERROR | %s %s | fork=%s\n", oplabel, pagelabel,
michael@0 466 LastErrMsg());
michael@0 467 exit(2);
michael@0 468 } else if (pid == 0) {
michael@0 469 volatile unsigned char scratch;
michael@0 470 switch (test) {
michael@0 471 case 0: scratch = *(volatile unsigned char *)opaddr; break;
michael@0 472 case 1: JumpTo(opaddr); break;
michael@0 473 case 2: *(volatile unsigned char *)opaddr = 0; break;
michael@0 474 default: abort();
michael@0 475 }
michael@0 476 (void)scratch;
michael@0 477 _exit(0);
michael@0 478 } else {
michael@0 479 int status;
michael@0 480 if (waitpid(pid, &status, 0) != pid) {
michael@0 481 printf("ERROR | %s %s | wait=%s\n", oplabel, pagelabel,
michael@0 482 LastErrMsg());
michael@0 483 exit(2);
michael@0 484 }
michael@0 485
michael@0 486 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
michael@0 487 if (should_succeed) {
michael@0 488 printf("TEST-PASS | %s %s\n", oplabel, pagelabel);
michael@0 489 } else {
michael@0 490 printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected successful exit\n",
michael@0 491 oplabel, pagelabel);
michael@0 492 failed = true;
michael@0 493 }
michael@0 494 } else if (WIFEXITED(status)) {
michael@0 495 printf("ERROR | %s %s | unexpected exit code %d\n",
michael@0 496 oplabel, pagelabel, WEXITSTATUS(status));
michael@0 497 exit(2);
michael@0 498 } else if (WIFSIGNALED(status)) {
michael@0 499 if (should_succeed) {
michael@0 500 printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected signal %d\n",
michael@0 501 oplabel, pagelabel, WTERMSIG(status));
michael@0 502 failed = true;
michael@0 503 } else {
michael@0 504 printf("TEST-PASS | %s %s | signal %d (as expected)\n",
michael@0 505 oplabel, pagelabel, WTERMSIG(status));
michael@0 506 }
michael@0 507 } else {
michael@0 508 printf("ERROR | %s %s | unexpected exit status %d\n",
michael@0 509 oplabel, pagelabel, status);
michael@0 510 exit(2);
michael@0 511 }
michael@0 512 }
michael@0 513 #endif
michael@0 514 }
michael@0 515 return failed;
michael@0 516 }
michael@0 517
michael@0 518 int
michael@0 519 main()
michael@0 520 {
michael@0 521 #ifdef _WIN32
michael@0 522 GetSystemInfo(&_sinfo);
michael@0 523 #else
michael@0 524 _pagesize = sysconf(_SC_PAGESIZE);
michael@0 525 #endif
michael@0 526
michael@0 527 uintptr_t ncontrol = ReserveNegativeControl();
michael@0 528 uintptr_t pcontrol = ReservePositiveControl();
michael@0 529 uintptr_t poison = ReservePoisonArea();
michael@0 530
michael@0 531 if (!ncontrol || !pcontrol || !poison)
michael@0 532 return 2;
michael@0 533
michael@0 534 bool failed = false;
michael@0 535 failed |= TestPage("negative control", ncontrol, 1);
michael@0 536 failed |= TestPage("positive control", pcontrol, 0);
michael@0 537 failed |= TestPage("poison area", poison, 0);
michael@0 538
michael@0 539 return failed ? 1 : 0;
michael@0 540 }

mercurial