1*35b5499dSMitch Phillips //===-- recoverable.cpp -----------------------------------------*- C++ -*-===//
2*35b5499dSMitch Phillips //
3*35b5499dSMitch Phillips // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*35b5499dSMitch Phillips // See https://llvm.org/LICENSE.txt for license information.
5*35b5499dSMitch Phillips // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*35b5499dSMitch Phillips //
7*35b5499dSMitch Phillips //===----------------------------------------------------------------------===//
8*35b5499dSMitch Phillips
9*35b5499dSMitch Phillips #include <atomic>
10*35b5499dSMitch Phillips #include <mutex>
11*35b5499dSMitch Phillips #include <regex>
12*35b5499dSMitch Phillips #include <string>
13*35b5499dSMitch Phillips #include <thread>
14*35b5499dSMitch Phillips #include <vector>
15*35b5499dSMitch Phillips
16*35b5499dSMitch Phillips #include "gwp_asan/common.h"
17*35b5499dSMitch Phillips #include "gwp_asan/crash_handler.h"
18*35b5499dSMitch Phillips #include "gwp_asan/tests/harness.h"
19*35b5499dSMitch Phillips
TEST_P(BacktraceGuardedPoolAllocator,MultipleDoubleFreeOnlyOneOutput)20*35b5499dSMitch Phillips TEST_P(BacktraceGuardedPoolAllocator, MultipleDoubleFreeOnlyOneOutput) {
21*35b5499dSMitch Phillips SCOPED_TRACE("");
22*35b5499dSMitch Phillips void *Ptr = AllocateMemory(GPA);
23*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
24*35b5499dSMitch Phillips // First time should generate a crash report.
25*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
26*35b5499dSMitch Phillips CheckOnlyOneGwpAsanCrash(GetOutputBuffer());
27*35b5499dSMitch Phillips ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free"));
28*35b5499dSMitch Phillips
29*35b5499dSMitch Phillips // Ensure the crash is only reported once.
30*35b5499dSMitch Phillips GetOutputBuffer().clear();
31*35b5499dSMitch Phillips for (size_t i = 0; i < 100; ++i) {
32*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
33*35b5499dSMitch Phillips ASSERT_TRUE(GetOutputBuffer().empty());
34*35b5499dSMitch Phillips }
35*35b5499dSMitch Phillips }
36*35b5499dSMitch Phillips
TEST_P(BacktraceGuardedPoolAllocator,MultipleInvalidFreeOnlyOneOutput)37*35b5499dSMitch Phillips TEST_P(BacktraceGuardedPoolAllocator, MultipleInvalidFreeOnlyOneOutput) {
38*35b5499dSMitch Phillips SCOPED_TRACE("");
39*35b5499dSMitch Phillips char *Ptr = static_cast<char *>(AllocateMemory(GPA));
40*35b5499dSMitch Phillips // First time should generate a crash report.
41*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr + 1);
42*35b5499dSMitch Phillips CheckOnlyOneGwpAsanCrash(GetOutputBuffer());
43*35b5499dSMitch Phillips ASSERT_NE(std::string::npos, GetOutputBuffer().find("Invalid (Wild) Free"));
44*35b5499dSMitch Phillips
45*35b5499dSMitch Phillips // Ensure the crash is only reported once.
46*35b5499dSMitch Phillips GetOutputBuffer().clear();
47*35b5499dSMitch Phillips for (size_t i = 0; i < 100; ++i) {
48*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr + 1);
49*35b5499dSMitch Phillips ASSERT_TRUE(GetOutputBuffer().empty());
50*35b5499dSMitch Phillips }
51*35b5499dSMitch Phillips }
52*35b5499dSMitch Phillips
TEST_P(BacktraceGuardedPoolAllocator,MultipleUseAfterFreeOnlyOneOutput)53*35b5499dSMitch Phillips TEST_P(BacktraceGuardedPoolAllocator, MultipleUseAfterFreeOnlyOneOutput) {
54*35b5499dSMitch Phillips SCOPED_TRACE("");
55*35b5499dSMitch Phillips void *Ptr = AllocateMemory(GPA);
56*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
57*35b5499dSMitch Phillips // First time should generate a crash report.
58*35b5499dSMitch Phillips TouchMemory(Ptr);
59*35b5499dSMitch Phillips ASSERT_NE(std::string::npos, GetOutputBuffer().find("Use After Free"));
60*35b5499dSMitch Phillips
61*35b5499dSMitch Phillips // Ensure the crash is only reported once.
62*35b5499dSMitch Phillips GetOutputBuffer().clear();
63*35b5499dSMitch Phillips for (size_t i = 0; i < 100; ++i) {
64*35b5499dSMitch Phillips TouchMemory(Ptr);
65*35b5499dSMitch Phillips ASSERT_TRUE(GetOutputBuffer().empty());
66*35b5499dSMitch Phillips }
67*35b5499dSMitch Phillips }
68*35b5499dSMitch Phillips
TEST_P(BacktraceGuardedPoolAllocator,MultipleBufferOverflowOnlyOneOutput)69*35b5499dSMitch Phillips TEST_P(BacktraceGuardedPoolAllocator, MultipleBufferOverflowOnlyOneOutput) {
70*35b5499dSMitch Phillips SCOPED_TRACE("");
71*35b5499dSMitch Phillips char *Ptr = static_cast<char *>(AllocateMemory(GPA));
72*35b5499dSMitch Phillips // First time should generate a crash report.
73*35b5499dSMitch Phillips TouchMemory(Ptr - 16);
74*35b5499dSMitch Phillips TouchMemory(Ptr + 16);
75*35b5499dSMitch Phillips CheckOnlyOneGwpAsanCrash(GetOutputBuffer());
76*35b5499dSMitch Phillips if (GetOutputBuffer().find("Buffer Overflow") == std::string::npos &&
77*35b5499dSMitch Phillips GetOutputBuffer().find("Buffer Underflow") == std::string::npos)
78*35b5499dSMitch Phillips FAIL() << "Failed to detect buffer underflow/overflow:\n"
79*35b5499dSMitch Phillips << GetOutputBuffer();
80*35b5499dSMitch Phillips
81*35b5499dSMitch Phillips // Ensure the crash is only reported once.
82*35b5499dSMitch Phillips GetOutputBuffer().clear();
83*35b5499dSMitch Phillips for (size_t i = 0; i < 100; ++i) {
84*35b5499dSMitch Phillips TouchMemory(Ptr - 16);
85*35b5499dSMitch Phillips TouchMemory(Ptr + 16);
86*35b5499dSMitch Phillips ASSERT_TRUE(GetOutputBuffer().empty()) << GetOutputBuffer();
87*35b5499dSMitch Phillips }
88*35b5499dSMitch Phillips }
89*35b5499dSMitch Phillips
TEST_P(BacktraceGuardedPoolAllocator,OneDoubleFreeOneUseAfterFree)90*35b5499dSMitch Phillips TEST_P(BacktraceGuardedPoolAllocator, OneDoubleFreeOneUseAfterFree) {
91*35b5499dSMitch Phillips SCOPED_TRACE("");
92*35b5499dSMitch Phillips void *Ptr = AllocateMemory(GPA);
93*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
94*35b5499dSMitch Phillips // First time should generate a crash report.
95*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
96*35b5499dSMitch Phillips CheckOnlyOneGwpAsanCrash(GetOutputBuffer());
97*35b5499dSMitch Phillips ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free"));
98*35b5499dSMitch Phillips
99*35b5499dSMitch Phillips // Ensure the crash is only reported once.
100*35b5499dSMitch Phillips GetOutputBuffer().clear();
101*35b5499dSMitch Phillips for (size_t i = 0; i < 100; ++i) {
102*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
103*35b5499dSMitch Phillips ASSERT_TRUE(GetOutputBuffer().empty());
104*35b5499dSMitch Phillips }
105*35b5499dSMitch Phillips }
106*35b5499dSMitch Phillips
107*35b5499dSMitch Phillips // We use double-free to detect that each slot can generate as single error.
108*35b5499dSMitch Phillips // Use-after-free would also be acceptable, but buffer-overflow wouldn't be, as
109*35b5499dSMitch Phillips // the random left/right alignment means that one right-overflow can disable
110*35b5499dSMitch Phillips // page protections, and a subsequent left-overflow of a slot that's on the
111*35b5499dSMitch Phillips // right hand side may not trap.
TEST_P(BacktraceGuardedPoolAllocator,OneErrorReportPerSlot)112*35b5499dSMitch Phillips TEST_P(BacktraceGuardedPoolAllocator, OneErrorReportPerSlot) {
113*35b5499dSMitch Phillips SCOPED_TRACE("");
114*35b5499dSMitch Phillips std::vector<void *> Ptrs;
115*35b5499dSMitch Phillips for (size_t i = 0; i < GPA.getAllocatorState()->MaxSimultaneousAllocations;
116*35b5499dSMitch Phillips ++i) {
117*35b5499dSMitch Phillips void *Ptr = AllocateMemory(GPA);
118*35b5499dSMitch Phillips ASSERT_NE(Ptr, nullptr);
119*35b5499dSMitch Phillips Ptrs.push_back(Ptr);
120*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
121*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
122*35b5499dSMitch Phillips CheckOnlyOneGwpAsanCrash(GetOutputBuffer());
123*35b5499dSMitch Phillips ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free"));
124*35b5499dSMitch Phillips // Ensure the crash from this slot is only reported once.
125*35b5499dSMitch Phillips GetOutputBuffer().clear();
126*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
127*35b5499dSMitch Phillips ASSERT_TRUE(GetOutputBuffer().empty());
128*35b5499dSMitch Phillips // Reset the buffer, as we're gonna move to the next allocation.
129*35b5499dSMitch Phillips GetOutputBuffer().clear();
130*35b5499dSMitch Phillips }
131*35b5499dSMitch Phillips
132*35b5499dSMitch Phillips // All slots should have been used. No further errors should occur.
133*35b5499dSMitch Phillips for (size_t i = 0; i < 100; ++i)
134*35b5499dSMitch Phillips ASSERT_EQ(AllocateMemory(GPA), nullptr);
135*35b5499dSMitch Phillips for (void *Ptr : Ptrs) {
136*35b5499dSMitch Phillips DeallocateMemory(GPA, Ptr);
137*35b5499dSMitch Phillips TouchMemory(Ptr);
138*35b5499dSMitch Phillips }
139*35b5499dSMitch Phillips ASSERT_TRUE(GetOutputBuffer().empty());
140*35b5499dSMitch Phillips }
141*35b5499dSMitch Phillips
singleAllocThrashTask(gwp_asan::GuardedPoolAllocator * GPA,std::atomic<bool> * StartingGun,unsigned NumIterations,unsigned Job,char * Ptr)142*35b5499dSMitch Phillips void singleAllocThrashTask(gwp_asan::GuardedPoolAllocator *GPA,
143*35b5499dSMitch Phillips std::atomic<bool> *StartingGun,
144*35b5499dSMitch Phillips unsigned NumIterations, unsigned Job, char *Ptr) {
145*35b5499dSMitch Phillips while (!*StartingGun) {
146*35b5499dSMitch Phillips // Wait for starting gun.
147*35b5499dSMitch Phillips }
148*35b5499dSMitch Phillips
149*35b5499dSMitch Phillips for (unsigned i = 0; i < NumIterations; ++i) {
150*35b5499dSMitch Phillips switch (Job) {
151*35b5499dSMitch Phillips case 0:
152*35b5499dSMitch Phillips DeallocateMemory(*GPA, Ptr);
153*35b5499dSMitch Phillips break;
154*35b5499dSMitch Phillips case 1:
155*35b5499dSMitch Phillips DeallocateMemory(*GPA, Ptr + 1);
156*35b5499dSMitch Phillips break;
157*35b5499dSMitch Phillips case 2:
158*35b5499dSMitch Phillips TouchMemory(Ptr);
159*35b5499dSMitch Phillips break;
160*35b5499dSMitch Phillips case 3:
161*35b5499dSMitch Phillips TouchMemory(Ptr - 16);
162*35b5499dSMitch Phillips TouchMemory(Ptr + 16);
163*35b5499dSMitch Phillips break;
164*35b5499dSMitch Phillips default:
165*35b5499dSMitch Phillips __builtin_trap();
166*35b5499dSMitch Phillips }
167*35b5499dSMitch Phillips }
168*35b5499dSMitch Phillips }
169*35b5499dSMitch Phillips
runInterThreadThrashingSingleAlloc(unsigned NumIterations,gwp_asan::GuardedPoolAllocator * GPA)170*35b5499dSMitch Phillips void runInterThreadThrashingSingleAlloc(unsigned NumIterations,
171*35b5499dSMitch Phillips gwp_asan::GuardedPoolAllocator *GPA) {
172*35b5499dSMitch Phillips std::atomic<bool> StartingGun{false};
173*35b5499dSMitch Phillips std::vector<std::thread> Threads;
174*35b5499dSMitch Phillips constexpr unsigned kNumThreads = 4;
175*35b5499dSMitch Phillips
176*35b5499dSMitch Phillips char *Ptr = static_cast<char *>(AllocateMemory(*GPA));
177*35b5499dSMitch Phillips
178*35b5499dSMitch Phillips for (unsigned i = 0; i < kNumThreads; ++i) {
179*35b5499dSMitch Phillips Threads.emplace_back(singleAllocThrashTask, GPA, &StartingGun,
180*35b5499dSMitch Phillips NumIterations, i, Ptr);
181*35b5499dSMitch Phillips }
182*35b5499dSMitch Phillips
183*35b5499dSMitch Phillips StartingGun = true;
184*35b5499dSMitch Phillips
185*35b5499dSMitch Phillips for (auto &T : Threads)
186*35b5499dSMitch Phillips T.join();
187*35b5499dSMitch Phillips }
188*35b5499dSMitch Phillips
TEST_P(BacktraceGuardedPoolAllocator,InterThreadThrashingSingleAlloc)189*35b5499dSMitch Phillips TEST_P(BacktraceGuardedPoolAllocator, InterThreadThrashingSingleAlloc) {
190*35b5499dSMitch Phillips SCOPED_TRACE("");
191*35b5499dSMitch Phillips constexpr unsigned kNumIterations = 100000;
192*35b5499dSMitch Phillips runInterThreadThrashingSingleAlloc(kNumIterations, &GPA);
193*35b5499dSMitch Phillips CheckOnlyOneGwpAsanCrash(GetOutputBuffer());
194*35b5499dSMitch Phillips }
195