LCOV - code coverage report
Current view: directory - layout/base/tests - TestPoisonArea.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 106 64 60.4 %
Date: 2012-06-02 Functions: 10 9 90.0 %

       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                 : }

Generated by: LCOV version 1.7