Poison.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /*
  6. * A poison value that can be used to fill a memory space with
  7. * an address that leads to a safe crash when dereferenced.
  8. */
  9. #include "mozilla/Poison.h"
  10. #include "mozilla/Assertions.h"
  11. #ifdef _WIN32
  12. # include <windows.h>
  13. #elif !defined(__OS2__)
  14. # include <unistd.h>
  15. # include <sys/mman.h>
  16. # ifndef MAP_ANON
  17. # ifdef MAP_ANONYMOUS
  18. # define MAP_ANON MAP_ANONYMOUS
  19. # else
  20. # error "Don't know how to get anonymous memory"
  21. # endif
  22. # endif
  23. #endif
  24. extern "C" {
  25. uintptr_t gMozillaPoisonValue;
  26. uintptr_t gMozillaPoisonBase;
  27. uintptr_t gMozillaPoisonSize;
  28. }
  29. // Freed memory is filled with a poison value, which we arrange to
  30. // form a pointer either to an always-unmapped region of the address
  31. // space, or to a page that has been reserved and rendered
  32. // inaccessible via OS primitives. See tests/TestPoisonArea.cpp for
  33. // extensive discussion of the requirements for this page. The code
  34. // from here to 'class FreeList' needs to be kept in sync with that
  35. // file.
  36. #ifdef _WIN32
  37. static void*
  38. ReserveRegion(uintptr_t aRegion, uintptr_t aSize)
  39. {
  40. return VirtualAlloc((void*)aRegion, aSize, MEM_RESERVE, PAGE_NOACCESS);
  41. }
  42. static void
  43. ReleaseRegion(void* aRegion, uintptr_t aSize)
  44. {
  45. VirtualFree(aRegion, aSize, MEM_RELEASE);
  46. }
  47. static bool
  48. ProbeRegion(uintptr_t aRegion, uintptr_t aSize)
  49. {
  50. SYSTEM_INFO sinfo;
  51. GetSystemInfo(&sinfo);
  52. if (aRegion >= (uintptr_t)sinfo.lpMaximumApplicationAddress &&
  53. aRegion + aSize >= (uintptr_t)sinfo.lpMaximumApplicationAddress) {
  54. return true;
  55. } else {
  56. return false;
  57. }
  58. }
  59. static uintptr_t
  60. GetDesiredRegionSize()
  61. {
  62. SYSTEM_INFO sinfo;
  63. GetSystemInfo(&sinfo);
  64. return sinfo.dwAllocationGranularity;
  65. }
  66. #define RESERVE_FAILED 0
  67. #elif defined(__OS2__)
  68. static void*
  69. ReserveRegion(uintptr_t aRegion, uintptr_t aSize)
  70. {
  71. // OS/2 doesn't support allocation at an arbitrary address,
  72. // so return an address that is known to be invalid.
  73. return (void*)0xFFFD0000;
  74. }
  75. static void
  76. ReleaseRegion(void* aRegion, uintptr_t aSize)
  77. {
  78. return;
  79. }
  80. static bool
  81. ProbeRegion(uintptr_t aRegion, uintptr_t aSize)
  82. {
  83. // There's no reliable way to probe an address in the system
  84. // arena other than by touching it and seeing if a trap occurs.
  85. return false;
  86. }
  87. static uintptr_t
  88. GetDesiredRegionSize()
  89. {
  90. // Page size is fixed at 4k.
  91. return 0x1000;
  92. }
  93. #define RESERVE_FAILED 0
  94. #else // Unix
  95. #include "mozilla/TaggedAnonymousMemory.h"
  96. static void*
  97. ReserveRegion(uintptr_t aRegion, uintptr_t aSize)
  98. {
  99. return MozTaggedAnonymousMmap(reinterpret_cast<void*>(aRegion), aSize,
  100. PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0,
  101. "poison");
  102. }
  103. static void
  104. ReleaseRegion(void* aRegion, uintptr_t aSize)
  105. {
  106. munmap(aRegion, aSize);
  107. }
  108. static bool
  109. ProbeRegion(uintptr_t aRegion, uintptr_t aSize)
  110. {
  111. #ifdef XP_SOLARIS
  112. if (posix_madvise(reinterpret_cast<void*>(aRegion), aSize, POSIX_MADV_NORMAL)) {
  113. #else
  114. if (madvise(reinterpret_cast<void*>(aRegion), aSize, MADV_NORMAL)) {
  115. #endif
  116. return true;
  117. } else {
  118. return false;
  119. }
  120. }
  121. static uintptr_t
  122. GetDesiredRegionSize()
  123. {
  124. return sysconf(_SC_PAGESIZE);
  125. }
  126. #define RESERVE_FAILED MAP_FAILED
  127. #endif // system dependencies
  128. static_assert(sizeof(uintptr_t) == 4 || sizeof(uintptr_t) == 8, "");
  129. static_assert(sizeof(uintptr_t) == sizeof(void*), "");
  130. static uintptr_t
  131. ReservePoisonArea(uintptr_t rgnsize)
  132. {
  133. if (sizeof(uintptr_t) == 8) {
  134. // Use the hardware-inaccessible region.
  135. // We have to avoid 64-bit constants and shifts by 32 bits, since this
  136. // code is compiled in 32-bit mode, although it is never executed there.
  137. return
  138. (((uintptr_t(0x7FFFFFFFu) << 31) << 1 | uintptr_t(0xF0DEAFFFu))
  139. & ~(rgnsize-1));
  140. }
  141. // First see if we can allocate the preferred poison address from the OS.
  142. uintptr_t candidate = (0xF0DEAFFF & ~(rgnsize-1));
  143. void* result = ReserveRegion(candidate, rgnsize);
  144. if (result == (void*)candidate) {
  145. // success - inaccessible page allocated
  146. return candidate;
  147. }
  148. // That didn't work, so see if the preferred address is within a range
  149. // of permanently inacessible memory.
  150. if (ProbeRegion(candidate, rgnsize)) {
  151. // success - selected page cannot be usable memory
  152. if (result != RESERVE_FAILED) {
  153. ReleaseRegion(result, rgnsize);
  154. }
  155. return candidate;
  156. }
  157. // The preferred address is already in use. Did the OS give us a
  158. // consolation prize?
  159. if (result != RESERVE_FAILED) {
  160. return uintptr_t(result);
  161. }
  162. // It didn't, so try to allocate again, without any constraint on
  163. // the address.
  164. result = ReserveRegion(0, rgnsize);
  165. if (result != RESERVE_FAILED) {
  166. return uintptr_t(result);
  167. }
  168. MOZ_CRASH("no usable poison region identified");
  169. }
  170. void
  171. mozPoisonValueInit()
  172. {
  173. gMozillaPoisonSize = GetDesiredRegionSize();
  174. gMozillaPoisonBase = ReservePoisonArea(gMozillaPoisonSize);
  175. if (gMozillaPoisonSize == 0) { // can't happen
  176. return;
  177. }
  178. gMozillaPoisonValue = gMozillaPoisonBase + gMozillaPoisonSize / 2 - 1;
  179. }