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