blob: e896c90f4a7b884f72f957aee3f14c4ee45c71bf [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.
Edgar Arriagad02148c2020-11-23 18:11:02 -080098 auto callback = [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name,
99 bool) {
Mitch Phillips242387d2020-02-11 16:08:17 -0800100 if (strcmp(name, "[anon:libc_malloc]") == 0 || strncmp(name, "[anon:scudo:", 12) == 0 ||
101 strncmp(name, "[anon:GWP-ASan", 14) == 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700102 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";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700116
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
125static 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.
146TEST(malloc_iterate, small_allocs) {
147#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800148 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700149 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
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800165 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700166#endif
167}
168
169// Verify that large allocs can be found properly.
170TEST(malloc_iterate, large_allocs) {
171#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800172 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700173 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
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800184 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700185#endif
186}
187
188// Verify that there are no crashes attempting to get pointers from
189// non-allocated pointers.
190TEST(malloc_iterate, invalid_pointers) {
191#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800192 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700193 TestDataType test_data = {};
194
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700195 // Only attempt to get memory data for maps that are not from the native allocator.
Edgar Arriagad02148c2020-11-23 18:11:02 -0800196 auto callback = [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name,
197 bool) {
Mitch Phillips242387d2020-02-11 16:08:17 -0800198 if (strcmp(name, "[anon:libc_malloc]") != 0 && strncmp(name, "[anon:scudo:", 12) != 0 &&
199 strncmp(name, "[anon:GWP-ASan", 14) != 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700200 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);
Sandeep Patil7d2aea02019-01-30 17:45:48 -0800213 }
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700214 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";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700233
234 ASSERT_EQ(0UL, test_data.total_allocated_bytes);
235#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800236 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700237#endif
238}
239
240TEST(malloc_iterate, malloc_disable_prevents_allocs) {
241#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800242 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700243 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
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800265 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700266#endif
267}