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