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