blob: c431a161d4ead86fda749e2744930edcc7255096 [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 Ferrisbfd3dc42018-10-15 10:02:38 -070034#include <vector>
35
Florian Mayer750dcd32022-04-15 15:54:47 -070036#include <android-base/test_utils.h>
Christopher Ferris8ea85af2019-08-16 11:07:50 -070037#include <async_safe/log.h>
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070038#include <procinfo/process_map.h>
39
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -080040#include "utils.h"
41
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -070042extern "C" void malloc_disable();
43extern "C" void malloc_enable();
44extern "C" int malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t base,
45 size_t size, void* arg), void* arg);
46
47struct AllocDataType {
48 void* ptr;
49 size_t size;
50 size_t size_reported;
51 size_t count;
52};
53
54struct TestDataType {
55 size_t total_allocated_bytes;
56 std::vector<AllocDataType> allocs;
57};
58
59static void AllocPtr(TestDataType* test_data, size_t size) {
60 test_data->allocs.resize(test_data->allocs.size() + 1);
61 AllocDataType* alloc = &test_data->allocs.back();
62 void* ptr = malloc(size);
63 ASSERT_TRUE(ptr != nullptr);
64 alloc->ptr = ptr;
65 alloc->size = malloc_usable_size(ptr);
66 alloc->size_reported = 0;
67 alloc->count = 0;
68}
69
70static void FreePtrs(TestDataType* test_data) {
71 for (size_t i = 0; i < test_data->allocs.size(); i++) {
72 free(test_data->allocs[i].ptr);
73 }
74}
75
76static void SavePointers(uintptr_t base, size_t size, void* data) {
77 TestDataType* test_data = reinterpret_cast<TestDataType*>(data);
78
79 test_data->total_allocated_bytes += size;
80
81 uintptr_t end;
82 if (__builtin_add_overflow(base, size, &end)) {
83 // Skip this entry.
84 return;
85 }
86
87 for (size_t i = 0; i < test_data->allocs.size(); i++) {
88 uintptr_t ptr = reinterpret_cast<uintptr_t>(test_data->allocs[i].ptr);
89 if (ptr >= base && ptr < end) {
90 test_data->allocs[i].count++;
91
92 uintptr_t max_size = end - ptr;
93 if (max_size > test_data->allocs[i].size) {
94 test_data->allocs[i].size_reported = test_data->allocs[i].size;
95 } else {
96 test_data->allocs[i].size_reported = max_size;
97 }
98 }
99 }
100}
101
102static void VerifyPtrs(TestDataType* test_data) {
103 test_data->total_allocated_bytes = 0;
104
Christopher Ferris88b2f0d2019-10-02 12:48:23 -0700105 // Find all of the maps that are from the native allocator.
Edgar Arriagad02148c2020-11-23 18:11:02 -0800106 auto callback = [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name,
107 bool) {
Mitch Phillips242387d2020-02-11 16:08:17 -0800108 if (strcmp(name, "[anon:libc_malloc]") == 0 || strncmp(name, "[anon:scudo:", 12) == 0 ||
109 strncmp(name, "[anon:GWP-ASan", 14) == 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700110 malloc_iterate(start, end - start, SavePointers, test_data);
111 }
112 };
113
114 std::vector<char> buffer(64 * 1024);
115
116 // Avoid doing allocations so that the maps don't change while looking
117 // for the pointers.
118 malloc_disable();
119 bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer.data(),
120 buffer.size(), callback);
121 malloc_enable();
122
123 ASSERT_TRUE(parsed) << "Failed to parse /proc/self/maps";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700124
125 for (size_t i = 0; i < test_data->allocs.size(); i++) {
126 EXPECT_EQ(1UL, test_data->allocs[i].count) << "Failed on size " << test_data->allocs[i].size;
127 if (test_data->allocs[i].count == 1) {
128 EXPECT_EQ(test_data->allocs[i].size, test_data->allocs[i].size_reported);
129 }
130 }
131}
132
133static void AllocateSizes(TestDataType* test_data, const std::vector<size_t>& sizes) {
134 static constexpr size_t kInitialAllocations = 40;
135 static constexpr size_t kNumAllocs = 50;
136 for (size_t size : sizes) {
137 // Verify that if the tcache is enabled, that tcache pointers
138 // are found by allocating and freeing 20 pointers (should be larger
139 // than the total number of cache entries).
140 for (size_t i = 0; i < kInitialAllocations; i++) {
141 void* ptr = malloc(size);
142 ASSERT_TRUE(ptr != nullptr);
143 memset(ptr, 0, size);
144 free(ptr);
145 }
146 for (size_t i = 0; i < kNumAllocs; i++) {
147 AllocPtr(test_data, size);
148 }
149 }
150}
151#endif
152
153// Verify that small allocs can be found properly.
154TEST(malloc_iterate, small_allocs) {
155#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800156 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700157 TestDataType test_data;
158
159 // Try to cycle through all of the different small bins.
160 // This is specific to the implementation of jemalloc and should be
161 // adjusted if a different native memory allocator is used.
162 std::vector<size_t> sizes{8, 16, 32, 48, 64, 80, 96, 112, 128, 160,
163 192, 224, 256, 320, 384, 448, 512, 640, 768, 896,
164 1024, 1280, 1536, 1792, 2048, 2560, 3072, 3584, 4096, 5120,
165 6144, 7168, 8192, 10240, 12288, 14336, 16384, 32768, 65536};
166 AllocateSizes(&test_data, sizes);
167
168 SCOPED_TRACE("");
169 VerifyPtrs(&test_data);
170
171 FreePtrs(&test_data);
172#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800173 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700174#endif
175}
176
177// Verify that large allocs can be found properly.
178TEST(malloc_iterate, large_allocs) {
179#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800180 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700181 TestDataType test_data;
182
183 // Try some larger sizes.
184 std::vector<size_t> sizes{131072, 262144, 524288, 1048576, 2097152};
185 AllocateSizes(&test_data, sizes);
186
187 SCOPED_TRACE("");
188 VerifyPtrs(&test_data);
189
190 FreePtrs(&test_data);
191#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800192 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700193#endif
194}
195
196// Verify that there are no crashes attempting to get pointers from
197// non-allocated pointers.
198TEST(malloc_iterate, invalid_pointers) {
199#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800200 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700201 TestDataType test_data = {};
202
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700203 // Only attempt to get memory data for maps that are not from the native allocator.
Edgar Arriagad02148c2020-11-23 18:11:02 -0800204 auto callback = [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name,
205 bool) {
Mitch Phillips242387d2020-02-11 16:08:17 -0800206 if (strcmp(name, "[anon:libc_malloc]") != 0 && strncmp(name, "[anon:scudo:", 12) != 0 &&
207 strncmp(name, "[anon:GWP-ASan", 14) != 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700208 size_t total = test_data.total_allocated_bytes;
209 malloc_iterate(start, end - start, SavePointers, &test_data);
210 total = test_data.total_allocated_bytes - total;
211 if (total > 0) {
212 char buffer[256];
213 int len = 0;
214 if (name[0] != '\0') {
215 len = async_safe_format_buffer(buffer, sizeof(buffer), "Failed on map %s: %zu\n", name,
216 total);
217 } else {
218 len = async_safe_format_buffer(buffer, sizeof(buffer),
219 "Failed on map anon:<%" PRIx64 "-%" PRIx64 ">: %zu\n",
220 start, end, total);
Sandeep Patil7d2aea02019-01-30 17:45:48 -0800221 }
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700222 if (len > 0) {
223 write(STDOUT_FILENO, buffer, len);
224 }
225 }
226 }
227 };
228
229 std::vector<char> buffer(64 * 1024);
230
231 // Need to make sure that there are no allocations while reading the
232 // maps. Otherwise, it might create a new map during this check and
233 // incorrectly think a map is empty while it actually includes real
234 // allocations.
235 malloc_disable();
236 bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer.data(),
237 buffer.size(), callback);
238 malloc_enable();
239
240 ASSERT_TRUE(parsed) << "Failed to parse /proc/self/maps";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700241
242 ASSERT_EQ(0UL, test_data.total_allocated_bytes);
243#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800244 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700245#endif
246}
247
248TEST(malloc_iterate, malloc_disable_prevents_allocs) {
249#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800250 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700251 pid_t pid;
252 if ((pid = fork()) == 0) {
253 malloc_disable();
254 void* ptr = malloc(1024);
255 if (ptr == nullptr) {
256 exit(1);
257 }
258 memset(ptr, 0, 1024);
259 exit(0);
260 }
261 ASSERT_NE(-1, pid);
262
263 // Expect that the malloc will hang forever, and that if the process
264 // does not return for two seconds, it is hung.
265 sleep(2);
266 pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG));
267 if (wait_pid <= 0) {
268 kill(pid, SIGKILL);
269 }
270 ASSERT_NE(-1, wait_pid) << "Unknown failure in waitpid.";
271 ASSERT_EQ(0, wait_pid) << "malloc_disable did not prevent allocation calls.";
272#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800273 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700274#endif
275}