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