blob: 8f4d7287967b88b9eeeafb4bd075771dc6f45b92 [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
Sharjeel Khan702f0502025-04-03 19:59:29 +000017// (b/291762537): This code uses malloc_usable_size(), and thus can't be
Sharjeel Khan5654c1f2025-05-07 17:03:27 +000018// built with _FORTIFY_SOURCE>=3.
19#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE >= 3
Sharjeel Khan702f0502025-04-03 19:59:29 +000020#undef _FORTIFY_SOURCE
21#define _FORTIFY_SOURCE 2
22#endif
23
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070024#include <gtest/gtest.h>
25
26#if defined(__BIONIC__)
27
Christopher Ferris8ea85af2019-08-16 11:07:50 -070028#include <inttypes.h>
29#include <stdint.h>
30#include <stdlib.h>
31#include <time.h>
32#include <unistd.h>
33
Christopher Ferris1e4dbae2025-06-12 00:04:17 +000034#include <ios>
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070035#include <vector>
36
Christopher Ferris1e4dbae2025-06-12 00:04:17 +000037#include <android-base/stringprintf.h>
Florian Mayer750dcd32022-04-15 15:54:47 -070038#include <android-base/test_utils.h>
Christopher Ferris8ea85af2019-08-16 11:07:50 -070039#include <async_safe/log.h>
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070040#include <procinfo/process_map.h>
41
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -080042#include "utils.h"
43
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070044extern "C" void malloc_disable();
45extern "C" void malloc_enable();
46extern "C" int malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t base,
47 size_t size, void* arg), void* arg);
48
49struct AllocDataType {
50 void* ptr;
51 size_t size;
52 size_t size_reported;
53 size_t count;
54};
55
Christopher Ferris1e4dbae2025-06-12 00:04:17 +000056constexpr size_t kMaxFailures = 10;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070057struct TestDataType {
58 size_t total_allocated_bytes;
59 std::vector<AllocDataType> allocs;
Christopher Ferris1e4dbae2025-06-12 00:04:17 +000060 // This data structure is updated during the callback for malloc_iterate.
61 // Since no allocations are allowed in this callback, we have an array to
62 // contain any errors encountered to be reported after the call is done.
63 AllocDataType failures[kMaxFailures] = {};
64 size_t num_failures = 0;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070065};
66
67static void AllocPtr(TestDataType* test_data, size_t size) {
68 test_data->allocs.resize(test_data->allocs.size() + 1);
69 AllocDataType* alloc = &test_data->allocs.back();
70 void* ptr = malloc(size);
71 ASSERT_TRUE(ptr != nullptr);
72 alloc->ptr = ptr;
73 alloc->size = malloc_usable_size(ptr);
74 alloc->size_reported = 0;
75 alloc->count = 0;
76}
77
78static void FreePtrs(TestDataType* test_data) {
79 for (size_t i = 0; i < test_data->allocs.size(); i++) {
80 free(test_data->allocs[i].ptr);
81 }
82}
83
84static void SavePointers(uintptr_t base, size_t size, void* data) {
85 TestDataType* test_data = reinterpret_cast<TestDataType*>(data);
86
87 test_data->total_allocated_bytes += size;
88
89 uintptr_t end;
90 if (__builtin_add_overflow(base, size, &end)) {
Christopher Ferris1e4dbae2025-06-12 00:04:17 +000091 size_t index = test_data->num_failures;
92 if (index < kMaxFailures) {
93 test_data->failures[index].ptr = reinterpret_cast<void*>(base);
94 test_data->failures[index].size = size;
95 test_data->num_failures++;
96 }
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070097 return;
98 }
99
100 for (size_t i = 0; i < test_data->allocs.size(); i++) {
101 uintptr_t ptr = reinterpret_cast<uintptr_t>(test_data->allocs[i].ptr);
102 if (ptr >= base && ptr < end) {
103 test_data->allocs[i].count++;
104
Christopher Ferris1e4dbae2025-06-12 00:04:17 +0000105 uintptr_t usable_size = end - ptr;
106 if (usable_size > test_data->allocs[i].size) {
107 // The usable size is greater than the allocated size, so set
108 // the size reported to allocated size.
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700109 test_data->allocs[i].size_reported = test_data->allocs[i].size;
110 } else {
Christopher Ferris1e4dbae2025-06-12 00:04:17 +0000111 test_data->allocs[i].size_reported = usable_size;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700112 }
Christopher Ferris1e4dbae2025-06-12 00:04:17 +0000113 break;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700114 }
115 }
116}
117
118static void VerifyPtrs(TestDataType* test_data) {
119 test_data->total_allocated_bytes = 0;
120
Christopher Ferris88b2f0d2019-10-02 12:48:23 -0700121 // Find all of the maps that are from the native allocator.
Edgar Arriagad02148c2020-11-23 18:11:02 -0800122 auto callback = [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name,
123 bool) {
Mitch Phillips242387d2020-02-11 16:08:17 -0800124 if (strcmp(name, "[anon:libc_malloc]") == 0 || strncmp(name, "[anon:scudo:", 12) == 0 ||
125 strncmp(name, "[anon:GWP-ASan", 14) == 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700126 malloc_iterate(start, end - start, SavePointers, test_data);
127 }
128 };
129
130 std::vector<char> buffer(64 * 1024);
131
132 // Avoid doing allocations so that the maps don't change while looking
133 // for the pointers.
134 malloc_disable();
135 bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer.data(),
136 buffer.size(), callback);
137 malloc_enable();
138
139 ASSERT_TRUE(parsed) << "Failed to parse /proc/self/maps";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700140
Christopher Ferris1e4dbae2025-06-12 00:04:17 +0000141 if (test_data->num_failures != 0) {
142 std::string error_msg;
143 for (size_t i = 0; i < test_data->num_failures; i++) {
144 error_msg +=
145 android::base::StringPrintf("Pointer overflow %p size %zu\n", test_data->failures[i].ptr,
146 test_data->failures[i].size);
147 }
148 FAIL() << error_msg;
149 }
150
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700151 for (size_t i = 0; i < test_data->allocs.size(); i++) {
Christopher Ferris1e4dbae2025-06-12 00:04:17 +0000152 EXPECT_EQ(1UL, test_data->allocs[i].count)
153 << "Unable to find allocation of size " << test_data->allocs[i].size;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700154 if (test_data->allocs[i].count == 1) {
155 EXPECT_EQ(test_data->allocs[i].size, test_data->allocs[i].size_reported);
156 }
157 }
158}
159
160static void AllocateSizes(TestDataType* test_data, const std::vector<size_t>& sizes) {
161 static constexpr size_t kInitialAllocations = 40;
162 static constexpr size_t kNumAllocs = 50;
163 for (size_t size : sizes) {
164 // Verify that if the tcache is enabled, that tcache pointers
165 // are found by allocating and freeing 20 pointers (should be larger
166 // than the total number of cache entries).
167 for (size_t i = 0; i < kInitialAllocations; i++) {
168 void* ptr = malloc(size);
169 ASSERT_TRUE(ptr != nullptr);
170 memset(ptr, 0, size);
171 free(ptr);
172 }
173 for (size_t i = 0; i < kNumAllocs; i++) {
174 AllocPtr(test_data, size);
175 }
176 }
177}
178#endif
179
180// Verify that small allocs can be found properly.
181TEST(malloc_iterate, small_allocs) {
182#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800183 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700184 TestDataType test_data;
185
186 // Try to cycle through all of the different small bins.
187 // This is specific to the implementation of jemalloc and should be
188 // adjusted if a different native memory allocator is used.
189 std::vector<size_t> sizes{8, 16, 32, 48, 64, 80, 96, 112, 128, 160,
190 192, 224, 256, 320, 384, 448, 512, 640, 768, 896,
191 1024, 1280, 1536, 1792, 2048, 2560, 3072, 3584, 4096, 5120,
192 6144, 7168, 8192, 10240, 12288, 14336, 16384, 32768, 65536};
193 AllocateSizes(&test_data, sizes);
194
195 SCOPED_TRACE("");
196 VerifyPtrs(&test_data);
197
198 FreePtrs(&test_data);
199#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800200 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700201#endif
202}
203
204// Verify that large allocs can be found properly.
205TEST(malloc_iterate, large_allocs) {
206#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800207 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700208 TestDataType test_data;
209
210 // Try some larger sizes.
211 std::vector<size_t> sizes{131072, 262144, 524288, 1048576, 2097152};
212 AllocateSizes(&test_data, sizes);
213
214 SCOPED_TRACE("");
215 VerifyPtrs(&test_data);
216
217 FreePtrs(&test_data);
218#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800219 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700220#endif
221}
222
223// Verify that there are no crashes attempting to get pointers from
224// non-allocated pointers.
225TEST(malloc_iterate, invalid_pointers) {
226#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800227 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700228 TestDataType test_data = {};
229
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700230 // Only attempt to get memory data for maps that are not from the native allocator.
Edgar Arriagad02148c2020-11-23 18:11:02 -0800231 auto callback = [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name,
232 bool) {
Mitch Phillips242387d2020-02-11 16:08:17 -0800233 if (strcmp(name, "[anon:libc_malloc]") != 0 && strncmp(name, "[anon:scudo:", 12) != 0 &&
234 strncmp(name, "[anon:GWP-ASan", 14) != 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700235 size_t total = test_data.total_allocated_bytes;
236 malloc_iterate(start, end - start, SavePointers, &test_data);
237 total = test_data.total_allocated_bytes - total;
238 if (total > 0) {
239 char buffer[256];
240 int len = 0;
241 if (name[0] != '\0') {
242 len = async_safe_format_buffer(buffer, sizeof(buffer), "Failed on map %s: %zu\n", name,
243 total);
244 } else {
245 len = async_safe_format_buffer(buffer, sizeof(buffer),
246 "Failed on map anon:<%" PRIx64 "-%" PRIx64 ">: %zu\n",
247 start, end, total);
Sandeep Patil7d2aea02019-01-30 17:45:48 -0800248 }
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700249 if (len > 0) {
250 write(STDOUT_FILENO, buffer, len);
251 }
252 }
253 }
254 };
255
256 std::vector<char> buffer(64 * 1024);
257
258 // Need to make sure that there are no allocations while reading the
259 // maps. Otherwise, it might create a new map during this check and
260 // incorrectly think a map is empty while it actually includes real
261 // allocations.
262 malloc_disable();
263 bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer.data(),
264 buffer.size(), callback);
265 malloc_enable();
266
267 ASSERT_TRUE(parsed) << "Failed to parse /proc/self/maps";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700268
269 ASSERT_EQ(0UL, test_data.total_allocated_bytes);
270#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800271 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700272#endif
273}
274
275TEST(malloc_iterate, malloc_disable_prevents_allocs) {
276#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800277 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700278 pid_t pid;
279 if ((pid = fork()) == 0) {
280 malloc_disable();
281 void* ptr = malloc(1024);
282 if (ptr == nullptr) {
283 exit(1);
284 }
285 memset(ptr, 0, 1024);
286 exit(0);
287 }
288 ASSERT_NE(-1, pid);
289
290 // Expect that the malloc will hang forever, and that if the process
291 // does not return for two seconds, it is hung.
292 sleep(2);
293 pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG));
294 if (wait_pid <= 0) {
295 kill(pid, SIGKILL);
296 }
297 ASSERT_NE(-1, wait_pid) << "Unknown failure in waitpid.";
298 ASSERT_EQ(0, wait_pid) << "malloc_disable did not prevent allocation calls.";
299#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800300 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700301#endif
302}