1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <fcntl.h>
18 #include <stdlib.h>
19 #include <sys/prctl.h>
20 #include <unistd.h>
21
22 #include <android-base/test_utils.h>
23
24 #include <gtest/gtest.h>
25
26 #include <memunreachable/memunreachable.h>
27
28 #include "bionic.h"
29
30 namespace android {
31
32 class HiddenPointer {
33 public:
34 // Since we're doing such a good job of hiding it, the static analyzer
35 // thinks that we're leaking this `malloc`. This is probably related to
36 // https://bugs.llvm.org/show_bug.cgi?id=34198. NOLINTNEXTLINE
HiddenPointer(size_t size=256)37 explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); }
~HiddenPointer()38 ~HiddenPointer() { Free(); }
Get()39 void* Get() { return reinterpret_cast<void*>(~ptr_); }
Free()40 void Free() {
41 free(Get());
42 Set(nullptr);
43 }
44
45 private:
Set(void * ptr)46 void Set(void* ptr) { ptr_ = ~reinterpret_cast<uintptr_t>(ptr); }
47 volatile uintptr_t ptr_;
48 };
49
50 // Trick the compiler into thinking a value on the stack is still referenced.
Ref(void ** ptr)51 static void Ref(void** ptr) {
52 void** volatile storage [[maybe_unused]];
53 storage = ptr;
54 }
55
56 class MemunreachableTest : public ::testing::Test {
57 protected:
SetUp()58 virtual void SetUp() {
59 // HWASan does not support malloc_disable, which is required by memunreachable.
60 SKIP_WITH_HWASAN;
61 CleanStack(8192);
62 CleanTcache();
63 }
64
TearDown()65 virtual void TearDown() {
66 CleanStack(8192);
67 CleanTcache();
68 }
69
70 // Allocate a buffer on the stack and zero it to make sure there are no
71 // stray pointers from old test runs.
CleanStack(size_t size)72 void __attribute__((noinline)) CleanStack(size_t size) {
73 void* buf = alloca(size);
74 memset(buf, 0, size);
75 Ref(&buf);
76 }
77
78 // Disable and re-enable malloc to flush the jemalloc tcache to make sure
79 // there are stray pointers from old test runs there.
CleanTcache()80 void CleanTcache() {
81 malloc_disable();
82 malloc_enable();
83 }
84 };
85
TEST_F(MemunreachableTest,clean)86 TEST_F(MemunreachableTest, clean) {
87 UnreachableMemoryInfo info;
88
89 ASSERT_TRUE(LogUnreachableMemory(true, 100));
90
91 ASSERT_TRUE(GetUnreachableMemory(info));
92 ASSERT_EQ(0U, info.leaks.size());
93 }
94
TEST_F(MemunreachableTest,stack)95 TEST_F(MemunreachableTest, stack) {
96 HiddenPointer hidden_ptr;
97
98 {
99 void* ptr = hidden_ptr.Get();
100 Ref(&ptr);
101
102 UnreachableMemoryInfo info;
103
104 ASSERT_TRUE(GetUnreachableMemory(info));
105 ASSERT_EQ(0U, info.leaks.size());
106
107 ptr = nullptr;
108 }
109
110 {
111 UnreachableMemoryInfo info;
112
113 ASSERT_TRUE(GetUnreachableMemory(info));
114 ASSERT_EQ(1U, info.leaks.size());
115 }
116
117 hidden_ptr.Free();
118
119 {
120 UnreachableMemoryInfo info;
121
122 ASSERT_TRUE(GetUnreachableMemory(info));
123 ASSERT_EQ(0U, info.leaks.size());
124 }
125 }
126
127 void* g_ptr;
128
TEST_F(MemunreachableTest,global)129 TEST_F(MemunreachableTest, global) {
130 HiddenPointer hidden_ptr;
131
132 g_ptr = hidden_ptr.Get();
133
134 {
135 UnreachableMemoryInfo info;
136
137 ASSERT_TRUE(GetUnreachableMemory(info));
138 ASSERT_EQ(0U, info.leaks.size());
139 }
140
141 g_ptr = nullptr;
142
143 {
144 UnreachableMemoryInfo info;
145
146 ASSERT_TRUE(GetUnreachableMemory(info));
147 ASSERT_EQ(1U, info.leaks.size());
148 }
149
150 hidden_ptr.Free();
151
152 {
153 UnreachableMemoryInfo info;
154
155 ASSERT_TRUE(GetUnreachableMemory(info));
156 ASSERT_EQ(0U, info.leaks.size());
157 }
158 }
159
TEST_F(MemunreachableTest,tls)160 TEST_F(MemunreachableTest, tls) {
161 HiddenPointer hidden_ptr;
162 pthread_key_t key;
163 pthread_key_create(&key, nullptr);
164
165 pthread_setspecific(key, hidden_ptr.Get());
166
167 {
168 UnreachableMemoryInfo info;
169
170 ASSERT_TRUE(GetUnreachableMemory(info));
171 ASSERT_EQ(0U, info.leaks.size());
172 }
173
174 pthread_setspecific(key, nullptr);
175
176 {
177 UnreachableMemoryInfo info;
178
179 ASSERT_TRUE(GetUnreachableMemory(info));
180 ASSERT_EQ(1U, info.leaks.size());
181 }
182
183 hidden_ptr.Free();
184
185 {
186 UnreachableMemoryInfo info;
187
188 ASSERT_TRUE(GetUnreachableMemory(info));
189 ASSERT_EQ(0U, info.leaks.size());
190 }
191
192 pthread_key_delete(key);
193 }
194
TEST_F(MemunreachableTest,twice)195 TEST_F(MemunreachableTest, twice) {
196 HiddenPointer hidden_ptr;
197
198 {
199 void* ptr = hidden_ptr.Get();
200 Ref(&ptr);
201
202 UnreachableMemoryInfo info;
203
204 ASSERT_TRUE(GetUnreachableMemory(info));
205 ASSERT_EQ(0U, info.leaks.size());
206
207 ptr = nullptr;
208 }
209
210 {
211 UnreachableMemoryInfo info;
212
213 ASSERT_TRUE(GetUnreachableMemory(info));
214 ASSERT_EQ(1U, info.leaks.size());
215 }
216
217 {
218 UnreachableMemoryInfo info;
219
220 ASSERT_TRUE(GetUnreachableMemory(info));
221 ASSERT_EQ(1U, info.leaks.size());
222 }
223
224 hidden_ptr.Free();
225
226 {
227 UnreachableMemoryInfo info;
228
229 ASSERT_TRUE(GetUnreachableMemory(info));
230 ASSERT_EQ(0U, info.leaks.size());
231 }
232 }
233
TEST_F(MemunreachableTest,log)234 TEST_F(MemunreachableTest, log) {
235 HiddenPointer hidden_ptr;
236
237 ASSERT_TRUE(LogUnreachableMemory(true, 100));
238
239 hidden_ptr.Free();
240
241 {
242 UnreachableMemoryInfo info;
243
244 ASSERT_TRUE(GetUnreachableMemory(info));
245 ASSERT_EQ(0U, info.leaks.size());
246 }
247 }
248
TEST_F(MemunreachableTest,notdumpable)249 TEST_F(MemunreachableTest, notdumpable) {
250 if (getuid() == 0) {
251 GTEST_SKIP() << "Not testable when running as root";
252 }
253
254 ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
255
256 HiddenPointer hidden_ptr;
257
258 EXPECT_FALSE(LogUnreachableMemory(true, 100));
259
260 ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
261 }
262
TEST_F(MemunreachableTest,leak_lots)263 TEST_F(MemunreachableTest, leak_lots) {
264 std::vector<HiddenPointer> hidden_ptrs;
265 hidden_ptrs.resize(1024);
266
267 ASSERT_TRUE(LogUnreachableMemory(true, 100));
268 }
269
TEST_F(MemunreachableTest,version)270 TEST_F(MemunreachableTest, version) {
271 UnreachableMemoryInfo info;
272 info.version = 1;
273
274 ASSERT_FALSE(GetUnreachableMemory(info));
275 ASSERT_EQ(0U, info.leaks.size());
276 }
277
278 } // namespace android
279