1 | //===-- crash_handler_api.cpp -----------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "gwp_asan/crash_handler.h" |
10 | #include "gwp_asan/guarded_pool_allocator.h" |
11 | #include "gwp_asan/stack_trace_compressor.h" |
12 | #include "gwp_asan/tests/harness.h" |
13 | |
14 | using Error = gwp_asan::Error; |
15 | using GuardedPoolAllocator = gwp_asan::GuardedPoolAllocator; |
16 | using AllocationMetadata = gwp_asan::AllocationMetadata; |
17 | using AllocatorState = gwp_asan::AllocatorState; |
18 | |
19 | class CrashHandlerAPITest : public ::testing::Test { |
20 | public: |
21 | void SetUp() override { setupState(); } |
22 | |
23 | protected: |
24 | size_t metadata(uintptr_t Addr, uintptr_t Size, bool IsDeallocated) { |
25 | // Should only be allocating the 0x3000, 0x5000, 0x7000, 0x9000 pages. |
26 | EXPECT_GE(Addr, 0x3000u); |
27 | EXPECT_LT(Addr, 0xa000u); |
28 | |
29 | size_t Slot = State.getNearestSlot(Ptr: Addr); |
30 | |
31 | Metadata[Slot].Addr = Addr; |
32 | Metadata[Slot].RequestedSize = Size; |
33 | Metadata[Slot].IsDeallocated = IsDeallocated; |
34 | Metadata[Slot].AllocationTrace.ThreadID = 123; |
35 | Metadata[Slot].DeallocationTrace.ThreadID = 321; |
36 | setupBacktraces(&Metadata[Slot]); |
37 | |
38 | return Slot; |
39 | } |
40 | |
41 | void setupState() { |
42 | State.GuardedPagePool = 0x2000; |
43 | State.GuardedPagePoolEnd = 0xc000; |
44 | InternalFaultAddr = State.GuardedPagePoolEnd - 0x10; |
45 | State.MaxSimultaneousAllocations = 4; // 0x3000, 0x5000, 0x7000, 0x9000. |
46 | State.PageSize = 0x1000; |
47 | } |
48 | |
49 | void setupBacktraces(AllocationMetadata *Meta) { |
50 | Meta->AllocationTrace.TraceSize = gwp_asan::compression::pack( |
51 | Unpacked: BacktraceConstants, UnpackedSize: kNumBacktraceConstants, |
52 | Packed: Meta->AllocationTrace.CompressedTrace, |
53 | PackedMaxSize: AllocationMetadata::kStackFrameStorageBytes); |
54 | |
55 | if (Meta->IsDeallocated) |
56 | Meta->DeallocationTrace.TraceSize = gwp_asan::compression::pack( |
57 | Unpacked: BacktraceConstants, UnpackedSize: kNumBacktraceConstants, |
58 | Packed: Meta->DeallocationTrace.CompressedTrace, |
59 | PackedMaxSize: AllocationMetadata::kStackFrameStorageBytes); |
60 | } |
61 | |
62 | void checkBacktrace(const AllocationMetadata *Meta, bool IsDeallocated) { |
63 | uintptr_t Buffer[kNumBacktraceConstants]; |
64 | size_t NumBacktraceConstants = kNumBacktraceConstants; |
65 | EXPECT_EQ(NumBacktraceConstants, __gwp_asan_get_allocation_trace( |
66 | AllocationMeta: Meta, Buffer, BufferLen: kNumBacktraceConstants)); |
67 | for (size_t i = 0; i < kNumBacktraceConstants; ++i) |
68 | EXPECT_EQ(Buffer[i], BacktraceConstants[i]); |
69 | |
70 | if (IsDeallocated) { |
71 | EXPECT_EQ(NumBacktraceConstants, |
72 | __gwp_asan_get_deallocation_trace(AllocationMeta: Meta, Buffer, |
73 | BufferLen: kNumBacktraceConstants)); |
74 | for (size_t i = 0; i < kNumBacktraceConstants; ++i) |
75 | EXPECT_EQ(Buffer[i], BacktraceConstants[i]); |
76 | } |
77 | } |
78 | |
79 | void checkMetadata(size_t Index, uintptr_t ErrorPtr) { |
80 | const AllocationMetadata *Meta = |
81 | __gwp_asan_get_metadata(State: &State, Metadata, ErrorPtr); |
82 | EXPECT_NE(nullptr, Meta); |
83 | EXPECT_EQ(Metadata[Index].Addr, __gwp_asan_get_allocation_address(AllocationMeta: Meta)); |
84 | EXPECT_EQ(Metadata[Index].RequestedSize, |
85 | __gwp_asan_get_allocation_size(AllocationMeta: Meta)); |
86 | EXPECT_EQ(Metadata[Index].AllocationTrace.ThreadID, |
87 | __gwp_asan_get_allocation_thread_id(AllocationMeta: Meta)); |
88 | |
89 | bool IsDeallocated = __gwp_asan_is_deallocated(AllocationMeta: Meta); |
90 | EXPECT_EQ(Metadata[Index].IsDeallocated, IsDeallocated); |
91 | checkBacktrace(Meta, IsDeallocated); |
92 | |
93 | if (!IsDeallocated) |
94 | return; |
95 | |
96 | EXPECT_EQ(Metadata[Index].DeallocationTrace.ThreadID, |
97 | __gwp_asan_get_deallocation_thread_id(AllocationMeta: Meta)); |
98 | } |
99 | |
100 | static constexpr size_t kNumBacktraceConstants = 4; |
101 | static uintptr_t BacktraceConstants[kNumBacktraceConstants]; |
102 | AllocatorState State = {}; |
103 | AllocationMetadata Metadata[4] = {}; |
104 | uintptr_t InternalFaultAddr; |
105 | }; |
106 | |
107 | uintptr_t CrashHandlerAPITest::BacktraceConstants[kNumBacktraceConstants] = { |
108 | 0xdeadbeef, 0xdeadc0de, 0xbadc0ffe, 0xcafef00d}; |
109 | |
110 | TEST_F(CrashHandlerAPITest, PointerNotMine) { |
111 | uintptr_t UnknownPtr = reinterpret_cast<uintptr_t>(&State); |
112 | |
113 | EXPECT_FALSE(__gwp_asan_error_is_mine(&State, 0)); |
114 | EXPECT_FALSE(__gwp_asan_error_is_mine(&State, UnknownPtr)); |
115 | |
116 | EXPECT_EQ(Error::UNKNOWN, __gwp_asan_diagnose_error(&State, Metadata, 0)); |
117 | EXPECT_EQ(Error::UNKNOWN, |
118 | __gwp_asan_diagnose_error(&State, Metadata, UnknownPtr)); |
119 | |
120 | EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, 0)); |
121 | EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, UnknownPtr)); |
122 | } |
123 | |
124 | TEST_F(CrashHandlerAPITest, PointerNotAllocated) { |
125 | uintptr_t FailureAddress = 0x9000; |
126 | |
127 | EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress)); |
128 | EXPECT_EQ(Error::UNKNOWN, |
129 | __gwp_asan_diagnose_error(&State, Metadata, FailureAddress)); |
130 | EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress)); |
131 | EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, FailureAddress)); |
132 | } |
133 | |
134 | TEST_F(CrashHandlerAPITest, DoubleFree) { |
135 | size_t Index = |
136 | metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ true); |
137 | uintptr_t FailureAddress = 0x7000; |
138 | |
139 | State.FailureType = Error::DOUBLE_FREE; |
140 | State.FailureAddress = FailureAddress; |
141 | |
142 | EXPECT_TRUE(__gwp_asan_error_is_mine(&State)); |
143 | EXPECT_EQ(Error::DOUBLE_FREE, |
144 | __gwp_asan_diagnose_error(&State, Metadata, 0x0)); |
145 | EXPECT_EQ(FailureAddress, |
146 | __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr)); |
147 | checkMetadata(Index, FailureAddress); |
148 | } |
149 | |
150 | TEST_F(CrashHandlerAPITest, InvalidFree) { |
151 | size_t Index = |
152 | metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ false); |
153 | uintptr_t FailureAddress = 0x7001; |
154 | |
155 | State.FailureType = Error::INVALID_FREE; |
156 | State.FailureAddress = FailureAddress; |
157 | |
158 | EXPECT_TRUE(__gwp_asan_error_is_mine(&State)); |
159 | EXPECT_EQ(Error::INVALID_FREE, |
160 | __gwp_asan_diagnose_error(&State, Metadata, 0x0)); |
161 | EXPECT_EQ(FailureAddress, |
162 | __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr)); |
163 | checkMetadata(Index, FailureAddress); |
164 | } |
165 | |
166 | TEST_F(CrashHandlerAPITest, InvalidFreeNoMetadata) { |
167 | uintptr_t FailureAddress = 0x7001; |
168 | |
169 | State.FailureType = Error::INVALID_FREE; |
170 | State.FailureAddress = FailureAddress; |
171 | |
172 | EXPECT_TRUE(__gwp_asan_error_is_mine(&State)); |
173 | EXPECT_EQ(Error::INVALID_FREE, |
174 | __gwp_asan_diagnose_error(&State, Metadata, 0x0)); |
175 | EXPECT_EQ(FailureAddress, |
176 | __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr)); |
177 | EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, FailureAddress)); |
178 | } |
179 | |
180 | TEST_F(CrashHandlerAPITest, UseAfterFree) { |
181 | size_t Index = |
182 | metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ true); |
183 | uintptr_t FailureAddress = 0x7001; |
184 | |
185 | EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress)); |
186 | EXPECT_EQ(Error::USE_AFTER_FREE, |
187 | __gwp_asan_diagnose_error(&State, Metadata, FailureAddress)); |
188 | EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress)); |
189 | checkMetadata(Index, FailureAddress); |
190 | } |
191 | |
192 | TEST_F(CrashHandlerAPITest, BufferOverflow) { |
193 | size_t Index = |
194 | metadata(/* Addr */ 0x5f00, /* Size */ 0x100, /* IsDeallocated */ false); |
195 | uintptr_t FailureAddress = 0x6000; |
196 | |
197 | EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress)); |
198 | EXPECT_EQ(Error::BUFFER_OVERFLOW, |
199 | __gwp_asan_diagnose_error(&State, Metadata, FailureAddress)); |
200 | EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress)); |
201 | checkMetadata(Index, FailureAddress); |
202 | } |
203 | |
204 | TEST_F(CrashHandlerAPITest, BufferUnderflow) { |
205 | size_t Index = |
206 | metadata(/* Addr */ 0x3000, /* Size */ 0x10, /* IsDeallocated*/ false); |
207 | uintptr_t FailureAddress = 0x2fff; |
208 | |
209 | EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress)); |
210 | EXPECT_EQ(Error::BUFFER_UNDERFLOW, |
211 | __gwp_asan_diagnose_error(&State, Metadata, FailureAddress)); |
212 | EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress)); |
213 | checkMetadata(Index, FailureAddress); |
214 | } |
215 | |