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