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