1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "gtest/gtest.h"
8 #include "replace_malloc_bridge.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/mozalloc.h"
11 #include "../../PHC.h"
13 using namespace mozilla
;
15 bool PHCInfoEq(phc::AddrInfo
& aInfo
, phc::AddrInfo::Kind aKind
, void* aBaseAddr
,
16 size_t aUsableSize
, bool aHasAllocStack
, bool aHasFreeStack
) {
17 return aInfo
.mKind
== aKind
&& aInfo
.mBaseAddr
== aBaseAddr
&&
18 aInfo
.mUsableSize
== aUsableSize
&&
19 // Proper stack traces will have at least 3 elements.
20 (aHasAllocStack
? (aInfo
.mAllocStack
->mLength
> 2)
21 : (aInfo
.mAllocStack
.isNothing())) &&
22 (aHasFreeStack
? (aInfo
.mFreeStack
->mLength
> 2)
23 : (aInfo
.mFreeStack
.isNothing()));
26 bool JeInfoEq(jemalloc_ptr_info_t
& aInfo
, PtrInfoTag aTag
, void* aAddr
,
27 size_t aSize
, arena_id_t arenaId
) {
28 return aInfo
.tag
== aTag
&& aInfo
.addr
== aAddr
&& aInfo
.size
== aSize
30 && aInfo
.arenaId
== arenaId
35 uint8_t* GetPHCAllocation(size_t aSize
, size_t aAlignment
= 1) {
36 // A crude but effective way to get a PHC allocation.
37 for (int i
= 0; i
< 2000000; i
++) {
38 void* p
= (aAlignment
== 1) ? moz_xmalloc(aSize
)
39 : moz_xmemalign(aAlignment
, aSize
);
40 if (ReplaceMalloc::IsPHCAllocation(p
, nullptr)) {
48 static const size_t kPageSize
= 4096;
50 TEST(PHC
, TestPHCAllocations
)
52 // First, check that allocations of various sizes all get put at the end of
53 // their page as expected. Also, check their sizes are as expected.
55 #define ASSERT_POS(n1, n2) \
56 p = (uint8_t*)moz_xrealloc(p, (n1)); \
57 ASSERT_EQ((reinterpret_cast<uintptr_t>(p) & (kPageSize - 1)), \
59 ASSERT_EQ(moz_malloc_usable_size(p), (n2));
61 uint8_t* p
= GetPHCAllocation(1);
63 MOZ_CRASH("failed to get a PHC allocation");
66 // On Win64 the smallest possible allocation is 16 bytes. On other platforms
68 #if defined(XP_WIN) && defined(HAVE_64BIT_BUILD)
76 ASSERT_POS(128U, 128U);
77 ASSERT_POS(256U, 256U);
78 ASSERT_POS(512U, 512U);
79 ASSERT_POS(1024U, 1024U);
80 ASSERT_POS(2048U, 2048U);
81 ASSERT_POS(4096U, 4096U);
87 // Second, do similar checking with allocations of various alignments. Also
88 // check that their sizes (which are different to allocations with normal
89 // alignment) are the same as the sizes of equivalent non-PHC allocations.
91 #define ASSERT_ALIGN(a1, a2) \
92 p = (uint8_t*)GetPHCAllocation(8, (a1)); \
93 ASSERT_EQ((reinterpret_cast<uintptr_t>(p) & (kPageSize - 1)), \
95 ASSERT_EQ(moz_malloc_usable_size(p), (a2)); \
97 p = (uint8_t*)moz_xmemalign((a1), 8); \
98 ASSERT_EQ(moz_malloc_usable_size(p), (a2)); \
101 // On Win64 the smallest possible allocation is 16 bytes. On other platforms
103 #if defined(XP_WIN) && defined(HAVE_64BIT_BUILD)
104 ASSERT_ALIGN(8U, 16U);
106 ASSERT_ALIGN(8U, 8U);
108 ASSERT_ALIGN(16U, 16U);
109 ASSERT_ALIGN(32U, 32U);
110 ASSERT_ALIGN(64U, 64U);
111 ASSERT_ALIGN(128U, 128U);
112 ASSERT_ALIGN(256U, 256U);
113 ASSERT_ALIGN(512U, 512U);
114 ASSERT_ALIGN(1024U, 1024U);
115 ASSERT_ALIGN(2048U, 2048U);
116 ASSERT_ALIGN(4096U, 4096U);
121 TEST(PHC
, TestPHCInfo
)
124 phc::AddrInfo phcInfo
;
125 jemalloc_ptr_info_t jeInfo
;
127 // Test a default AddrInfo.
128 ASSERT_TRUE(PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::Unknown
, nullptr, 0ul,
131 // Test some non-PHC allocation addresses.
132 ASSERT_FALSE(ReplaceMalloc::IsPHCAllocation(nullptr, &phcInfo
));
133 ASSERT_TRUE(PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::Unknown
, nullptr, 0,
135 ASSERT_FALSE(ReplaceMalloc::IsPHCAllocation(&stackVar
, &phcInfo
));
136 ASSERT_TRUE(PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::Unknown
, nullptr, 0,
139 uint8_t* p
= GetPHCAllocation(32);
141 MOZ_CRASH("failed to get a PHC allocation");
144 // Test an in-use PHC allocation: first byte within it.
145 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
, &phcInfo
));
147 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::InUsePage
, p
, 32ul, true, false));
148 ASSERT_EQ(moz_malloc_usable_size(p
), 32ul);
149 jemalloc_ptr_info(p
, &jeInfo
);
150 ASSERT_TRUE(JeInfoEq(jeInfo
, TagLiveAlloc
, p
, 32, 0));
152 // Test an in-use PHC allocation: last byte within it.
153 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
+ 31, &phcInfo
));
155 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::InUsePage
, p
, 32ul, true, false));
156 ASSERT_EQ(moz_malloc_usable_size(p
+ 31), 32ul);
157 jemalloc_ptr_info(p
+ 31, &jeInfo
);
158 ASSERT_TRUE(JeInfoEq(jeInfo
, TagLiveAlloc
, p
, 32, 0));
160 // Test an in-use PHC allocation: last byte before it.
161 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
- 1, &phcInfo
));
163 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::InUsePage
, p
, 32ul, true, false));
164 ASSERT_EQ(moz_malloc_usable_size(p
- 1), 0ul);
165 jemalloc_ptr_info(p
- 1, &jeInfo
);
166 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
168 // Test an in-use PHC allocation: first byte on its allocation page.
169 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
+ 32 - kPageSize
, &phcInfo
));
171 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::InUsePage
, p
, 32ul, true, false));
172 jemalloc_ptr_info(p
+ 32 - kPageSize
, &jeInfo
);
173 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
175 // Test an in-use PHC allocation: first byte in the following guard page.
176 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
+ 32, &phcInfo
));
178 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::GuardPage
, p
, 32ul, true, false));
179 jemalloc_ptr_info(p
+ 32, &jeInfo
);
180 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
182 // Test an in-use PHC allocation: last byte in the lower half of the
183 // following guard page.
185 ReplaceMalloc::IsPHCAllocation(p
+ 32 + (kPageSize
/ 2 - 1), &phcInfo
));
187 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::GuardPage
, p
, 32ul, true, false));
188 jemalloc_ptr_info(p
+ 32 + (kPageSize
/ 2 - 1), &jeInfo
);
189 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
191 // Test an in-use PHC allocation: last byte in the preceding guard page.
192 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
+ 31 - kPageSize
, &phcInfo
));
194 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::GuardPage
, p
, 32ul, true, false));
195 jemalloc_ptr_info(p
+ 31 - kPageSize
, &jeInfo
);
196 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
198 // Test an in-use PHC allocation: first byte in the upper half of the
199 // preceding guard page.
200 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(
201 p
+ 31 - kPageSize
- (kPageSize
/ 2 - 1), &phcInfo
));
203 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::GuardPage
, p
, 32ul, true, false));
204 jemalloc_ptr_info(p
+ 31 - kPageSize
- (kPageSize
/ 2 - 1), &jeInfo
);
205 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
209 // Test a freed PHC allocation: first byte within it.
210 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
, &phcInfo
));
212 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::FreedPage
, p
, 32ul, true, true));
213 jemalloc_ptr_info(p
, &jeInfo
);
214 ASSERT_TRUE(JeInfoEq(jeInfo
, TagFreedAlloc
, p
, 32, 0));
216 // Test a freed PHC allocation: last byte within it.
217 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
+ 31, &phcInfo
));
219 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::FreedPage
, p
, 32ul, true, true));
220 jemalloc_ptr_info(p
+ 31, &jeInfo
);
221 ASSERT_TRUE(JeInfoEq(jeInfo
, TagFreedAlloc
, p
, 32, 0));
223 // Test a freed PHC allocation: last byte before it.
224 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
- 1, &phcInfo
));
226 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::FreedPage
, p
, 32ul, true, true));
227 jemalloc_ptr_info(p
- 1, &jeInfo
);
228 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
230 // Test a freed PHC allocation: first byte on its allocation page.
231 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
+ 32 - kPageSize
, &phcInfo
));
233 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::FreedPage
, p
, 32ul, true, true));
234 jemalloc_ptr_info(p
+ 32 - kPageSize
, &jeInfo
);
235 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
237 // Test a freed PHC allocation: first byte in the following guard page.
238 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
+ 32, &phcInfo
));
240 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::GuardPage
, p
, 32ul, true, true));
241 jemalloc_ptr_info(p
+ 32, &jeInfo
);
242 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
244 // Test a freed PHC allocation: last byte in the lower half of the following
247 ReplaceMalloc::IsPHCAllocation(p
+ 32 + (kPageSize
/ 2 - 1), &phcInfo
));
249 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::GuardPage
, p
, 32ul, true, true));
250 jemalloc_ptr_info(p
+ 32 + (kPageSize
/ 2 - 1), &jeInfo
);
251 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
253 // Test a freed PHC allocation: last byte in the preceding guard page.
254 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p
+ 31 - kPageSize
, &phcInfo
));
256 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::GuardPage
, p
, 32ul, true, true));
257 jemalloc_ptr_info(p
+ 31 - kPageSize
, &jeInfo
);
258 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
260 // Test a freed PHC allocation: first byte in the upper half of the preceding
262 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(
263 p
+ 31 - kPageSize
- (kPageSize
/ 2 - 1), &phcInfo
));
265 PHCInfoEq(phcInfo
, phc::AddrInfo::Kind::GuardPage
, p
, 32ul, true, true));
266 jemalloc_ptr_info(p
+ 31 - kPageSize
- (kPageSize
/ 2 - 1), &jeInfo
);
267 ASSERT_TRUE(JeInfoEq(jeInfo
, TagUnknown
, nullptr, 0, 0));
269 // There are no tests for `mKind == NeverAllocatedPage` because it's not
270 // possible to reliably get ahold of such a page.
273 TEST(PHC
, TestPHCDisabling
)
275 uint8_t* p
= GetPHCAllocation(32);
276 uint8_t* q
= GetPHCAllocation(32);
278 MOZ_CRASH("failed to get a PHC allocation");
281 ASSERT_TRUE(ReplaceMalloc::IsPHCEnabledOnCurrentThread());
282 ReplaceMalloc::DisablePHCOnCurrentThread();
283 ASSERT_FALSE(ReplaceMalloc::IsPHCEnabledOnCurrentThread());
285 // Test realloc() on a PHC allocation while PHC is disabled on the thread.
286 uint8_t* p2
= (uint8_t*)realloc(p
, 128);
287 // The small realloc is fulfilled within the same page, but it does move.
288 ASSERT_TRUE(p2
== p
- 96);
289 ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p2
, nullptr));
290 uint8_t* p3
= (uint8_t*)realloc(p2
, 8192);
291 // The big realloc is not in-place, and the result is not a PHC allocation.
292 ASSERT_TRUE(p3
!= p2
);
293 ASSERT_FALSE(ReplaceMalloc::IsPHCAllocation(p3
, nullptr));
296 // Test free() on a PHC allocation while PHC is disabled on the thread.
299 // These must not be PHC allocations.
300 uint8_t* r
= GetPHCAllocation(32); // This will fail.
303 ReplaceMalloc::ReenablePHCOnCurrentThread();
304 ASSERT_TRUE(ReplaceMalloc::IsPHCEnabledOnCurrentThread());
306 // If it really was reenabled we should be able to get PHC allocations
308 uint8_t* s
= GetPHCAllocation(32); // This should succeed.