blob: 6a82eb69920e1ca6dc6775072d3d2c0f2e845f5b [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) {
Christopher Ferris88b2f0d2019-10-02 12:48:23 -070099 if (strcmp(name, "[anon:libc_malloc]") == 0 || strncmp(name, "[anon:scudo:", 12) == 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700100 malloc_iterate(start, end - start, SavePointers, test_data);
101 }
102 };
103
104 std::vector<char> buffer(64 * 1024);
105
106 // Avoid doing allocations so that the maps don't change while looking
107 // for the pointers.
108 malloc_disable();
109 bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer.data(),
110 buffer.size(), callback);
111 malloc_enable();
112
113 ASSERT_TRUE(parsed) << "Failed to parse /proc/self/maps";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700114
115 for (size_t i = 0; i < test_data->allocs.size(); i++) {
116 EXPECT_EQ(1UL, test_data->allocs[i].count) << "Failed on size " << test_data->allocs[i].size;
117 if (test_data->allocs[i].count == 1) {
118 EXPECT_EQ(test_data->allocs[i].size, test_data->allocs[i].size_reported);
119 }
120 }
121}
122
123static void AllocateSizes(TestDataType* test_data, const std::vector<size_t>& sizes) {
124 static constexpr size_t kInitialAllocations = 40;
125 static constexpr size_t kNumAllocs = 50;
126 for (size_t size : sizes) {
127 // Verify that if the tcache is enabled, that tcache pointers
128 // are found by allocating and freeing 20 pointers (should be larger
129 // than the total number of cache entries).
130 for (size_t i = 0; i < kInitialAllocations; i++) {
131 void* ptr = malloc(size);
132 ASSERT_TRUE(ptr != nullptr);
133 memset(ptr, 0, size);
134 free(ptr);
135 }
136 for (size_t i = 0; i < kNumAllocs; i++) {
137 AllocPtr(test_data, size);
138 }
139 }
140}
141#endif
142
143// Verify that small allocs can be found properly.
144TEST(malloc_iterate, small_allocs) {
145#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800146 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700147 TestDataType test_data;
148
149 // Try to cycle through all of the different small bins.
150 // This is specific to the implementation of jemalloc and should be
151 // adjusted if a different native memory allocator is used.
152 std::vector<size_t> sizes{8, 16, 32, 48, 64, 80, 96, 112, 128, 160,
153 192, 224, 256, 320, 384, 448, 512, 640, 768, 896,
154 1024, 1280, 1536, 1792, 2048, 2560, 3072, 3584, 4096, 5120,
155 6144, 7168, 8192, 10240, 12288, 14336, 16384, 32768, 65536};
156 AllocateSizes(&test_data, sizes);
157
158 SCOPED_TRACE("");
159 VerifyPtrs(&test_data);
160
161 FreePtrs(&test_data);
162#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800163 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700164#endif
165}
166
167// Verify that large allocs can be found properly.
168TEST(malloc_iterate, large_allocs) {
169#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800170 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700171 TestDataType test_data;
172
173 // Try some larger sizes.
174 std::vector<size_t> sizes{131072, 262144, 524288, 1048576, 2097152};
175 AllocateSizes(&test_data, sizes);
176
177 SCOPED_TRACE("");
178 VerifyPtrs(&test_data);
179
180 FreePtrs(&test_data);
181#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800182 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700183#endif
184}
185
186// Verify that there are no crashes attempting to get pointers from
187// non-allocated pointers.
188TEST(malloc_iterate, invalid_pointers) {
189#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800190 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700191 TestDataType test_data = {};
192
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700193 // Only attempt to get memory data for maps that are not from the native allocator.
194 auto callback = [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) {
Christopher Ferris88b2f0d2019-10-02 12:48:23 -0700195 if (strcmp(name, "[anon:libc_malloc]") != 0 && strncmp(name, "[anon:scudo:", 12) != 0) {
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700196 size_t total = test_data.total_allocated_bytes;
197 malloc_iterate(start, end - start, SavePointers, &test_data);
198 total = test_data.total_allocated_bytes - total;
199 if (total > 0) {
200 char buffer[256];
201 int len = 0;
202 if (name[0] != '\0') {
203 len = async_safe_format_buffer(buffer, sizeof(buffer), "Failed on map %s: %zu\n", name,
204 total);
205 } else {
206 len = async_safe_format_buffer(buffer, sizeof(buffer),
207 "Failed on map anon:<%" PRIx64 "-%" PRIx64 ">: %zu\n",
208 start, end, total);
Sandeep Patil7d2aea02019-01-30 17:45:48 -0800209 }
Christopher Ferris8ea85af2019-08-16 11:07:50 -0700210 if (len > 0) {
211 write(STDOUT_FILENO, buffer, len);
212 }
213 }
214 }
215 };
216
217 std::vector<char> buffer(64 * 1024);
218
219 // Need to make sure that there are no allocations while reading the
220 // maps. Otherwise, it might create a new map during this check and
221 // incorrectly think a map is empty while it actually includes real
222 // allocations.
223 malloc_disable();
224 bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer.data(),
225 buffer.size(), callback);
226 malloc_enable();
227
228 ASSERT_TRUE(parsed) << "Failed to parse /proc/self/maps";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700229
230 ASSERT_EQ(0UL, test_data.total_allocated_bytes);
231#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800232 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700233#endif
234}
235
236TEST(malloc_iterate, malloc_disable_prevents_allocs) {
237#if defined(__BIONIC__)
Evgenii Stepanovacd6f4f2018-11-06 16:48:27 -0800238 SKIP_WITH_HWASAN;
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700239 pid_t pid;
240 if ((pid = fork()) == 0) {
241 malloc_disable();
242 void* ptr = malloc(1024);
243 if (ptr == nullptr) {
244 exit(1);
245 }
246 memset(ptr, 0, 1024);
247 exit(0);
248 }
249 ASSERT_NE(-1, pid);
250
251 // Expect that the malloc will hang forever, and that if the process
252 // does not return for two seconds, it is hung.
253 sleep(2);
254 pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG));
255 if (wait_pid <= 0) {
256 kill(pid, SIGKILL);
257 }
258 ASSERT_NE(-1, wait_pid) << "Unknown failure in waitpid.";
259 ASSERT_EQ(0, wait_pid) << "malloc_disable did not prevent allocation calls.";
260#else
Elliott Hughesbcaa4542019-03-08 15:20:23 -0800261 GTEST_SKIP() << "bionic-only test";
Christopher Ferrisbfd3dc42018-10-15 10:02:38 -0700262#endif
263}