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