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