Coverage Report

Created: 2018-09-25 14:53

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