| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* |
| * A poison value that can be used to fill a memory space with |
| * an address that leads to a safe crash when dereferenced. |
| */ |
| |
| #include "mozilla/Poison.h" |
| |
| #include "mozilla/Assertions.h" |
| #if defined(STARBOARD) |
| # include "starboard/memory.h" |
| #elif defined(_WIN32) |
| # include <windows.h> |
| #elif !defined(__OS2__) |
| # include <unistd.h> |
| # include <sys/mman.h> |
| # ifndef MAP_ANON |
| # ifdef MAP_ANONYMOUS |
| # define MAP_ANON MAP_ANONYMOUS |
| # else |
| # error "Don't know how to get anonymous memory" |
| # endif |
| # endif |
| #endif |
| |
| extern "C" { |
| uintptr_t gMozillaPoisonValue; |
| uintptr_t gMozillaPoisonBase; |
| uintptr_t gMozillaPoisonSize; |
| } |
| |
| // Freed memory is filled with a poison value, which we arrange to |
| // form a pointer either to an always-unmapped region of the address |
| // space, or to a page that has been reserved and rendered |
| // inaccessible via OS primitives. See tests/TestPoisonArea.cpp for |
| // extensive discussion of the requirements for this page. The code |
| // from here to 'class FreeList' needs to be kept in sync with that |
| // file. |
| |
| #if defined(STARBOARD) |
| static void * |
| ReserveRegion(uintptr_t region, uintptr_t size) |
| { |
| // Not all Starboard platforms support allocation at an arbitrary address, |
| // so return an address that is known to be invalid. |
| return (void*)0xFFFD0000; |
| } |
| |
| static void |
| ReleaseRegion(void *region, uintptr_t size) |
| { |
| return; |
| } |
| |
| static bool |
| ProbeRegion(uintptr_t region, uintptr_t size) |
| { |
| // There's no reliable way to probe an address in the system |
| // arena other than by touching it and seeing if a trap occurs. |
| return false; |
| } |
| |
| static uintptr_t |
| GetDesiredRegionSize() |
| { |
| return SB_MEMORY_PAGE_SIZE; |
| } |
| |
| #define RESERVE_FAILED 0 |
| |
| #elif defined(_WIN32) |
| static void * |
| ReserveRegion(uintptr_t region, uintptr_t size) |
| { |
| return VirtualAlloc((void *)region, size, MEM_RESERVE, PAGE_NOACCESS); |
| } |
| |
| static void |
| ReleaseRegion(void *region, uintptr_t size) |
| { |
| VirtualFree(region, size, MEM_RELEASE); |
| } |
| |
| static bool |
| ProbeRegion(uintptr_t region, uintptr_t size) |
| { |
| SYSTEM_INFO sinfo; |
| GetSystemInfo(&sinfo); |
| if (region >= (uintptr_t)sinfo.lpMaximumApplicationAddress && |
| region + size >= (uintptr_t)sinfo.lpMaximumApplicationAddress) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static uintptr_t |
| GetDesiredRegionSize() |
| { |
| SYSTEM_INFO sinfo; |
| GetSystemInfo(&sinfo); |
| return sinfo.dwAllocationGranularity; |
| } |
| |
| #define RESERVE_FAILED 0 |
| |
| #elif defined(__OS2__) |
| static void * |
| ReserveRegion(uintptr_t region, uintptr_t size) |
| { |
| // OS/2 doesn't support allocation at an arbitrary address, |
| // so return an address that is known to be invalid. |
| return (void*)0xFFFD0000; |
| } |
| |
| static void |
| ReleaseRegion(void *region, uintptr_t size) |
| { |
| return; |
| } |
| |
| static bool |
| ProbeRegion(uintptr_t region, uintptr_t size) |
| { |
| // There's no reliable way to probe an address in the system |
| // arena other than by touching it and seeing if a trap occurs. |
| return false; |
| } |
| |
| static uintptr_t |
| GetDesiredRegionSize() |
| { |
| // Page size is fixed at 4k. |
| return 0x1000; |
| } |
| |
| #define RESERVE_FAILED 0 |
| |
| #else // Unix |
| |
| static void * |
| ReserveRegion(uintptr_t region, uintptr_t size) |
| { |
| return mmap(reinterpret_cast<void*>(region), size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0); |
| } |
| |
| static void |
| ReleaseRegion(void *region, uintptr_t size) |
| { |
| munmap(region, size); |
| } |
| |
| static bool |
| ProbeRegion(uintptr_t region, uintptr_t size) |
| { |
| if (madvise(reinterpret_cast<void*>(region), size, MADV_NORMAL)) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static uintptr_t |
| GetDesiredRegionSize() |
| { |
| return sysconf(_SC_PAGESIZE); |
| } |
| |
| #define RESERVE_FAILED MAP_FAILED |
| |
| #endif // system dependencies |
| |
| MOZ_STATIC_ASSERT(sizeof(uintptr_t) == 4 || sizeof(uintptr_t) == 8, ""); |
| MOZ_STATIC_ASSERT(sizeof(uintptr_t) == sizeof(void *), ""); |
| |
| static uintptr_t |
| ReservePoisonArea(uintptr_t rgnsize) |
| { |
| if (sizeof(uintptr_t) == 8) { |
| // Use the hardware-inaccessible region. |
| // We have to avoid 64-bit constants and shifts by 32 bits, since this |
| // code is compiled in 32-bit mode, although it is never executed there. |
| return |
| (((uintptr_t(0x7FFFFFFFu) << 31) << 1 | uintptr_t(0xF0DEAFFFu)) |
| & ~(rgnsize-1)); |
| |
| } else { |
| // First see if we can allocate the preferred poison address from the OS. |
| uintptr_t candidate = (0xF0DEAFFF & ~(rgnsize-1)); |
| void *result = ReserveRegion(candidate, rgnsize); |
| if (result == (void *)candidate) { |
| // success - inaccessible page allocated |
| return candidate; |
| } |
| |
| // That didn't work, so see if the preferred address is within a range |
| // of permanently inacessible memory. |
| if (ProbeRegion(candidate, rgnsize)) { |
| // success - selected page cannot be usable memory |
| if (result != RESERVE_FAILED) |
| ReleaseRegion(result, rgnsize); |
| return candidate; |
| } |
| |
| // The preferred address is already in use. Did the OS give us a |
| // consolation prize? |
| if (result != RESERVE_FAILED) { |
| return uintptr_t(result); |
| } |
| |
| // It didn't, so try to allocate again, without any constraint on |
| // the address. |
| result = ReserveRegion(0, rgnsize); |
| if (result != RESERVE_FAILED) { |
| return uintptr_t(result); |
| } |
| |
| // no usable poison region identified |
| MOZ_CRASH(); |
| return 0; |
| } |
| } |
| |
| void |
| mozPoisonValueInit() |
| { |
| gMozillaPoisonSize = GetDesiredRegionSize(); |
| gMozillaPoisonBase = ReservePoisonArea(gMozillaPoisonSize); |
| |
| if (gMozillaPoisonSize == 0) // can't happen |
| return; |
| |
| gMozillaPoisonValue = gMozillaPoisonBase + gMozillaPoisonSize/2 - 1; |
| } |