blob: 738a57b2b4fbf3d99b42963f83de9cae957d2d21 [file] [log] [blame]
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -07001/*
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
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070017#include <gtest/gtest.h>
18
19#if defined(__BIONIC__)
20
Christopher Ferris8ea85af2019-08-16 11:07:50 -070021#include <inttypes.h>
22#include <stdint.h>
23#include <stdlib.h>
24#include <time.h>
25#include <unistd.h>
26
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070027#include <vector>
28
Christopher Ferris8ea85af2019-08-16 11:07:50 -070029#include <async_safe/log.h>
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070030#include <procinfo/process_map.h>
31
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -080032#include "utils.h"
33
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070034extern "C" void malloc_disable();
35extern "C" void malloc_enable();
36extern "C" int malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t base,
37 size_t size, void* arg), void* arg);
38
39struct AllocDataType {
40 void* ptr;
41 size_t size;
42 size_t size_reported;
43 size_t count;
44};
45
46struct TestDataType {
47 size_t total_allocated_bytes;
48 std::vector<AllocDataType> allocs;
49};
50
51static 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
62static 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
68static 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
94static void VerifyPtrs(TestDataType* test_data) {
95 test_data->total_allocated_bytes = 0;
96
Christopher Ferris88b2f0d2019-10-02 12:48:23 -070097 // Find all of the maps that are from the native allocator.
Christopher Ferris8ea85af2019-08-16 11:07:50 -070098 auto callback = [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) {
Mitch Phillips242387d2020-02-11 16:08:17 -080099 if (strcmp(name, "[anon:libc_malloc]") == 0 || strncmp(name, "[anon:scudo:", 12) == 0 ||
100 strncmp(name, "[anon:GWP-ASan", 14) == 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700101 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";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700115
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
124static 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.
145TEST(malloc_iterate, small_allocs) {
146#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800147 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700148 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
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800164 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700165#endif
166}
167
168// Verify that large allocs can be found properly.
169TEST(malloc_iterate, large_allocs) {
170#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800171 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700172 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
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800183 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700184#endif
185}
186
187// Verify that there are no crashes attempting to get pointers from
188// non-allocated pointers.
189TEST(malloc_iterate, invalid_pointers) {
190#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800191 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700192 TestDataType test_data = {};
193
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700194 // 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) {
Mitch Phillips242387d2020-02-11 16:08:17 -0800196 if (strcmp(name, "[anon:libc_malloc]") != 0 && strncmp(name, "[anon:scudo:", 12) != 0 &&
197 strncmp(name, "[anon:GWP-ASan", 14) != 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700198 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);
Sandeep Patil7d2aea02019-01-30 17:45:48 -0800211 }
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700212 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";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700231
232 ASSERT_EQ(0UL, test_data.total_allocated_bytes);
233#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800234 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700235#endif
236}
237
238TEST(malloc_iterate, malloc_disable_prevents_allocs) {
239#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800240 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700241 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
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800263 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700264#endif
265}