• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AllocPtr(TestDataType * test_data,size_t size)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 
FreePtrs(TestDataType * test_data)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 
SavePointers(uintptr_t base,size_t size,void * data)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 
VerifyPtrs(TestDataType * test_data)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 
AllocateSizes(TestDataType * test_data,const std::vector<size_t> & sizes)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.
TEST(malloc_iterate,small_allocs)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.
TEST(malloc_iterate,large_allocs)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.
TEST(malloc_iterate,invalid_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 
TEST(malloc_iterate,malloc_disable_prevents_allocs)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