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