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 void** volatile storage; 51 storage = ptr; 52 } 53 54 class MemunreachableTest : public ::testing::Test { 55 protected: 56 virtual void SetUp() { 57 CleanStack(8192); 58 CleanTcache(); 59 } 60 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. 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. 76 void CleanTcache() { 77 malloc_disable(); 78 malloc_enable(); 79 } 80 }; 81 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 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 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 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 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 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 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 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 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