Home | History | Annotate | Download | only in tests
      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