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 <gtest/gtest.h>
23 
24 #include <memunreachable/memunreachable.h>
25 
26 #include "bionic.h"
27 
28 namespace android {
29 
30 class HiddenPointer {
31  public:
32   // Since we're doing such a good job of hiding it, the static analyzer
33   // thinks that we're leaking this `malloc`. This is probably related to
34   // https://bugs.llvm.org/show_bug.cgi?id=34198. NOLINTNEXTLINE
HiddenPointer(size_t size=256)35   explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); }
~HiddenPointer()36   ~HiddenPointer() { Free(); }
Get()37   void* Get() { return reinterpret_cast<void*>(~ptr_); }
Free()38   void Free() {
39     free(Get());
40     Set(nullptr);
41   }
42 
43  private:
Set(void * ptr)44   void Set(void* ptr) { ptr_ = ~reinterpret_cast<uintptr_t>(ptr); }
45   volatile uintptr_t ptr_;
46 };
47 
48 // Trick the compiler into thinking a value on the stack is still referenced.
Ref(void ** ptr)49 static void Ref(void** ptr) {
50   void** volatile storage;
51   storage = ptr;
52 }
53 
54 class MemunreachableTest : public ::testing::Test {
55  protected:
SetUp()56   virtual void SetUp() {
57     CleanStack(8192);
58     CleanTcache();
59   }
60 
TearDown()61   virtual void TearDown() {
62     CleanStack(8192);
63     CleanTcache();
64   }
65 
66   // Allocate a buffer on the stack and zero it to make sure there are no
67   // stray pointers from old test runs.
CleanStack(size_t size)68   void __attribute__((noinline)) CleanStack(size_t size) {
69     void* buf = alloca(size);
70     memset(buf, 0, size);
71     Ref(&buf);
72   }
73 
74   // Disable and re-enable malloc to flush the jemalloc tcache to make sure
75   // there are stray pointers from old test runs there.
CleanTcache()76   void CleanTcache() {
77     malloc_disable();
78     malloc_enable();
79   }
80 };
81 
TEST_F(MemunreachableTest,clean)82 TEST_F(MemunreachableTest, clean) {
83   UnreachableMemoryInfo info;
84 
85   ASSERT_TRUE(LogUnreachableMemory(true, 100));
86 
87   ASSERT_TRUE(GetUnreachableMemory(info));
88   ASSERT_EQ(0U, info.leaks.size());
89 }
90 
TEST_F(MemunreachableTest,stack)91 TEST_F(MemunreachableTest, stack) {
92   HiddenPointer hidden_ptr;
93 
94   {
95     void* ptr = hidden_ptr.Get();
96     Ref(&ptr);
97 
98     UnreachableMemoryInfo info;
99 
100     ASSERT_TRUE(GetUnreachableMemory(info));
101     ASSERT_EQ(0U, info.leaks.size());
102 
103     ptr = nullptr;
104   }
105 
106   {
107     UnreachableMemoryInfo info;
108 
109     ASSERT_TRUE(GetUnreachableMemory(info));
110     ASSERT_EQ(1U, info.leaks.size());
111   }
112 
113   hidden_ptr.Free();
114 
115   {
116     UnreachableMemoryInfo info;
117 
118     ASSERT_TRUE(GetUnreachableMemory(info));
119     ASSERT_EQ(0U, info.leaks.size());
120   }
121 }
122 
123 void* g_ptr;
124 
TEST_F(MemunreachableTest,global)125 TEST_F(MemunreachableTest, global) {
126   HiddenPointer hidden_ptr;
127 
128   g_ptr = hidden_ptr.Get();
129 
130   {
131     UnreachableMemoryInfo info;
132 
133     ASSERT_TRUE(GetUnreachableMemory(info));
134     ASSERT_EQ(0U, info.leaks.size());
135   }
136 
137   g_ptr = nullptr;
138 
139   {
140     UnreachableMemoryInfo info;
141 
142     ASSERT_TRUE(GetUnreachableMemory(info));
143     ASSERT_EQ(1U, info.leaks.size());
144   }
145 
146   hidden_ptr.Free();
147 
148   {
149     UnreachableMemoryInfo info;
150 
151     ASSERT_TRUE(GetUnreachableMemory(info));
152     ASSERT_EQ(0U, info.leaks.size());
153   }
154 }
155 
TEST_F(MemunreachableTest,tls)156 TEST_F(MemunreachableTest, tls) {
157   HiddenPointer hidden_ptr;
158   pthread_key_t key;
159   pthread_key_create(&key, nullptr);
160 
161   pthread_setspecific(key, hidden_ptr.Get());
162 
163   {
164     UnreachableMemoryInfo info;
165 
166     ASSERT_TRUE(GetUnreachableMemory(info));
167     ASSERT_EQ(0U, info.leaks.size());
168   }
169 
170   pthread_setspecific(key, nullptr);
171 
172   {
173     UnreachableMemoryInfo info;
174 
175     ASSERT_TRUE(GetUnreachableMemory(info));
176     ASSERT_EQ(1U, info.leaks.size());
177   }
178 
179   hidden_ptr.Free();
180 
181   {
182     UnreachableMemoryInfo info;
183 
184     ASSERT_TRUE(GetUnreachableMemory(info));
185     ASSERT_EQ(0U, info.leaks.size());
186   }
187 
188   pthread_key_delete(key);
189 }
190 
TEST_F(MemunreachableTest,twice)191 TEST_F(MemunreachableTest, twice) {
192   HiddenPointer hidden_ptr;
193 
194   {
195     void* ptr = hidden_ptr.Get();
196     Ref(&ptr);
197 
198     UnreachableMemoryInfo info;
199 
200     ASSERT_TRUE(GetUnreachableMemory(info));
201     ASSERT_EQ(0U, info.leaks.size());
202 
203     ptr = nullptr;
204   }
205 
206   {
207     UnreachableMemoryInfo info;
208 
209     ASSERT_TRUE(GetUnreachableMemory(info));
210     ASSERT_EQ(1U, info.leaks.size());
211   }
212 
213   {
214     UnreachableMemoryInfo info;
215 
216     ASSERT_TRUE(GetUnreachableMemory(info));
217     ASSERT_EQ(1U, info.leaks.size());
218   }
219 
220   hidden_ptr.Free();
221 
222   {
223     UnreachableMemoryInfo info;
224 
225     ASSERT_TRUE(GetUnreachableMemory(info));
226     ASSERT_EQ(0U, info.leaks.size());
227   }
228 }
229 
TEST_F(MemunreachableTest,log)230 TEST_F(MemunreachableTest, log) {
231   HiddenPointer hidden_ptr;
232 
233   ASSERT_TRUE(LogUnreachableMemory(true, 100));
234 
235   hidden_ptr.Free();
236 
237   {
238     UnreachableMemoryInfo info;
239 
240     ASSERT_TRUE(GetUnreachableMemory(info));
241     ASSERT_EQ(0U, info.leaks.size());
242   }
243 }
244 
TEST_F(MemunreachableTest,notdumpable)245 TEST_F(MemunreachableTest, notdumpable) {
246   if (getuid() == 0) {
247     // TODO(ccross): make this a skipped test when gtest supports them
248     printf("[ SKIP     ] Not testable when running as root\n");
249     return;
250   }
251 
252   ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
253 
254   HiddenPointer hidden_ptr;
255 
256   EXPECT_FALSE(LogUnreachableMemory(true, 100));
257 
258   ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
259 }
260 
TEST_F(MemunreachableTest,leak_lots)261 TEST_F(MemunreachableTest, leak_lots) {
262   std::vector<HiddenPointer> hidden_ptrs;
263   hidden_ptrs.resize(1024);
264 
265   ASSERT_TRUE(LogUnreachableMemory(true, 100));
266 }
267 
TEST_F(MemunreachableTest,version)268 TEST_F(MemunreachableTest, version) {
269   UnreachableMemoryInfo info;
270   info.version = 1;
271 
272   ASSERT_FALSE(GetUnreachableMemory(info));
273   ASSERT_EQ(0U, info.leaks.size());
274 }
275 
276 }  // namespace android
277