Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2018 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 <stdint.h>
     18 #include <stdlib.h>
     19 #include <time.h>
     20 
     21 #include <gtest/gtest.h>
     22 
     23 #if defined(__BIONIC__)
     24 
     25 #include <vector>
     26 
     27 #include <procinfo/process_map.h>
     28 
     29 #include "utils.h"
     30 
     31 extern "C" void malloc_disable();
     32 extern "C" void malloc_enable();
     33 extern "C" int malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t base,
     34                               size_t size, void* arg), void* arg);
     35 
     36 struct AllocDataType {
     37   void* ptr;
     38   size_t size;
     39   size_t size_reported;
     40   size_t count;
     41 };
     42 
     43 struct TestDataType {
     44   size_t total_allocated_bytes;
     45   std::vector<AllocDataType> allocs;
     46 };
     47 
     48 static void AllocPtr(TestDataType* test_data, size_t size) {
     49   test_data->allocs.resize(test_data->allocs.size() + 1);
     50   AllocDataType* alloc = &test_data->allocs.back();
     51   void* ptr = malloc(size);
     52   ASSERT_TRUE(ptr != nullptr);
     53   alloc->ptr = ptr;
     54   alloc->size = malloc_usable_size(ptr);
     55   alloc->size_reported = 0;
     56   alloc->count = 0;
     57 }
     58 
     59 static void FreePtrs(TestDataType* test_data) {
     60   for (size_t i = 0; i < test_data->allocs.size(); i++) {
     61     free(test_data->allocs[i].ptr);
     62   }
     63 }
     64 
     65 static void SavePointers(uintptr_t base, size_t size, void* data) {
     66   TestDataType* test_data = reinterpret_cast<TestDataType*>(data);
     67 
     68   test_data->total_allocated_bytes += size;
     69 
     70   uintptr_t end;
     71   if (__builtin_add_overflow(base, size, &end)) {
     72     // Skip this entry.
     73     return;
     74   }
     75 
     76   for (size_t i = 0; i < test_data->allocs.size(); i++) {
     77     uintptr_t ptr = reinterpret_cast<uintptr_t>(test_data->allocs[i].ptr);
     78     if (ptr >= base && ptr < end) {
     79       test_data->allocs[i].count++;
     80 
     81       uintptr_t max_size = end - ptr;
     82       if (max_size > test_data->allocs[i].size) {
     83         test_data->allocs[i].size_reported = test_data->allocs[i].size;
     84       } else {
     85         test_data->allocs[i].size_reported = max_size;
     86       }
     87     }
     88   }
     89 }
     90 
     91 static void VerifyPtrs(TestDataType* test_data) {
     92   test_data->total_allocated_bytes = 0;
     93 
     94   // Find all of the maps that are [anon:libc_malloc].
     95   ASSERT_TRUE(android::procinfo::ReadMapFile(
     96       "/proc/self/maps",
     97       [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) {
     98         if (std::string(name) == "[anon:libc_malloc]") {
     99           malloc_disable();
    100           malloc_iterate(start, end - start, SavePointers, test_data);
    101           malloc_enable();
    102         }
    103       }));
    104 
    105   for (size_t i = 0; i < test_data->allocs.size(); i++) {
    106     EXPECT_EQ(1UL, test_data->allocs[i].count) << "Failed on size " << test_data->allocs[i].size;
    107     if (test_data->allocs[i].count == 1) {
    108       EXPECT_EQ(test_data->allocs[i].size, test_data->allocs[i].size_reported);
    109     }
    110   }
    111 }
    112 
    113 static void AllocateSizes(TestDataType* test_data, const std::vector<size_t>& sizes) {
    114   static constexpr size_t kInitialAllocations = 40;
    115   static constexpr size_t kNumAllocs = 50;
    116   for (size_t size : sizes) {
    117     // Verify that if the tcache is enabled, that tcache pointers
    118     // are found by allocating and freeing 20 pointers (should be larger
    119     // than the total number of cache entries).
    120     for (size_t i = 0; i < kInitialAllocations; i++) {
    121       void* ptr = malloc(size);
    122       ASSERT_TRUE(ptr != nullptr);
    123       memset(ptr, 0, size);
    124       free(ptr);
    125     }
    126     for (size_t i = 0; i < kNumAllocs; i++) {
    127       AllocPtr(test_data, size);
    128     }
    129   }
    130 }
    131 #endif
    132 
    133 // Verify that small allocs can be found properly.
    134 TEST(malloc_iterate, small_allocs) {
    135 #if defined(__BIONIC__)
    136   SKIP_WITH_HWASAN;
    137   TestDataType test_data;
    138 
    139   // Try to cycle through all of the different small bins.
    140   // This is specific to the implementation of jemalloc and should be
    141   // adjusted if a different native memory allocator is used.
    142   std::vector<size_t> sizes{8,    16,   32,   48,    64,    80,    96,    112,   128,  160,
    143                             192,  224,  256,  320,   384,   448,   512,   640,   768,  896,
    144                             1024, 1280, 1536, 1792,  2048,  2560,  3072,  3584,  4096, 5120,
    145                             6144, 7168, 8192, 10240, 12288, 14336, 16384, 32768, 65536};
    146   AllocateSizes(&test_data, sizes);
    147 
    148   SCOPED_TRACE("");
    149   VerifyPtrs(&test_data);
    150 
    151   FreePtrs(&test_data);
    152 #else
    153   GTEST_SKIP() << "bionic-only test";
    154 #endif
    155 }
    156 
    157 // Verify that large allocs can be found properly.
    158 TEST(malloc_iterate, large_allocs) {
    159 #if defined(__BIONIC__)
    160   SKIP_WITH_HWASAN;
    161   TestDataType test_data;
    162 
    163   // Try some larger sizes.
    164   std::vector<size_t> sizes{131072, 262144, 524288, 1048576, 2097152};
    165   AllocateSizes(&test_data, sizes);
    166 
    167   SCOPED_TRACE("");
    168   VerifyPtrs(&test_data);
    169 
    170   FreePtrs(&test_data);
    171 #else
    172   GTEST_SKIP() << "bionic-only test";
    173 #endif
    174 }
    175 
    176 // Verify that there are no crashes attempting to get pointers from
    177 // non-allocated pointers.
    178 TEST(malloc_iterate, invalid_pointers) {
    179 #if defined(__BIONIC__)
    180   SKIP_WITH_HWASAN;
    181   TestDataType test_data = {};
    182 
    183   // Find all of the maps that are not [anon:libc_malloc].
    184   ASSERT_TRUE(android::procinfo::ReadMapFile(
    185       "/proc/self/maps",
    186       [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) {
    187         if (std::string(name) != "[anon:libc_malloc]") {
    188           malloc_disable();
    189           malloc_iterate(start, end - start, SavePointers, &test_data);
    190           malloc_enable();
    191         }
    192       }));
    193 
    194   ASSERT_EQ(0UL, test_data.total_allocated_bytes);
    195 #else
    196   GTEST_SKIP() << "bionic-only test";
    197 #endif
    198 }
    199 
    200 TEST(malloc_iterate, malloc_disable_prevents_allocs) {
    201 #if defined(__BIONIC__)
    202   SKIP_WITH_HWASAN;
    203   pid_t pid;
    204   if ((pid = fork()) == 0) {
    205     malloc_disable();
    206     void* ptr = malloc(1024);
    207     if (ptr == nullptr) {
    208       exit(1);
    209     }
    210     memset(ptr, 0, 1024);
    211     exit(0);
    212   }
    213   ASSERT_NE(-1, pid);
    214 
    215   // Expect that the malloc will hang forever, and that if the process
    216   // does not return for two seconds, it is hung.
    217   sleep(2);
    218   pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG));
    219   if (wait_pid <= 0) {
    220     kill(pid, SIGKILL);
    221   }
    222   ASSERT_NE(-1, wait_pid) << "Unknown failure in waitpid.";
    223   ASSERT_EQ(0, wait_pid) << "malloc_disable did not prevent allocation calls.";
    224 #else
    225   GTEST_SKIP() << "bionic-only test";
    226 #endif
    227 }
    228