1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <fcntl.h>
30 #include <malloc.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <unistd.h>
35
36 #include <string>
37 #include <vector>
38
39 #include <gtest/gtest.h>
40
41 #include <private/bionic_malloc_dispatch.h>
42 #include <tests/utils.h>
43
44 __BEGIN_DECLS
45
46 void get_malloc_leak_info(uint8_t**, size_t*, size_t*, size_t*, size_t*);
47 void free_malloc_leak_info(uint8_t*);
48 int malloc_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
49 void malloc_enable();
50 void malloc_disable();
51 ssize_t malloc_backtrace(void*, uintptr_t*, size_t);
52
53 __END_DECLS
54
55 class MallocHooksTest : public ::testing::Test {
56 protected:
SetUp()57 void SetUp() override {
58 ASSERT_EQ(0, setenv("LIBC_HOOKS_ENABLE", "1", true));
59 initialized_ = false;
60 }
61
TearDown()62 void TearDown() override {
63 if (initialized_) {
64 Reset();
65 }
66 ASSERT_EQ(0, unsetenv("LIBC_HOOKS_ENABLE"));
67 }
68
Init()69 void Init() {
70 initialized_ = true;
71 malloc_hook_called_ = false;
72 free_hook_called_ = false;
73 realloc_hook_called_ = false;
74 memalign_hook_called_ = false;
75
76 void_arg_ = nullptr;
77
78 orig_malloc_hook_ = __malloc_hook;
79 orig_free_hook_ = __free_hook;
80 orig_realloc_hook_ = __realloc_hook;
81 orig_memalign_hook_ = __memalign_hook;
82 }
83
Reset()84 void Reset() {
85 __malloc_hook = orig_malloc_hook_;
86 __free_hook = orig_free_hook_;
87 __realloc_hook = orig_realloc_hook_;
88 __memalign_hook = orig_memalign_hook_;
89 }
90
91 void Exec(std::vector<const char*> args);
92 void RunTest(std::string test_name);
93
94 public:
95 bool initialized_;
96
97 int fd_;
98 std::string raw_output_;
99 pid_t pid_;
100
101 static bool malloc_hook_called_;
102 static bool free_hook_called_;
103 static bool realloc_hook_called_;
104 static bool memalign_hook_called_;
105 static const void* void_arg_;
106
107 static void* (*orig_malloc_hook_)(size_t, const void*);
108 static void (*orig_free_hook_)(void*, const void*);
109 static void* (*orig_realloc_hook_)(void*, size_t, const void*);
110 static void* (*orig_memalign_hook_)(size_t, size_t, const void*);
111
112 static void* test_malloc_hook(size_t, const void*);
113 static void* test_realloc_hook(void* ptr, size_t, const void*);
114 static void* test_memalign_hook(size_t, size_t, const void*);
115 static void test_free_hook(void*, const void*);
116 };
117
118 bool MallocHooksTest::malloc_hook_called_;
119 bool MallocHooksTest::free_hook_called_;
120 bool MallocHooksTest::realloc_hook_called_;
121 bool MallocHooksTest::memalign_hook_called_;
122 const void* MallocHooksTest::void_arg_;
123
124 void* (*MallocHooksTest::orig_malloc_hook_)(size_t, const void*);
125 void (*MallocHooksTest::orig_free_hook_)(void*, const void*);
126 void* (*MallocHooksTest::orig_realloc_hook_)(void*, size_t, const void*);
127 void* (*MallocHooksTest::orig_memalign_hook_)(size_t, size_t, const void*);
128
GetExe(std::string * exe_name)129 static void GetExe(std::string* exe_name) {
130 char path[PATH_MAX];
131 ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path));
132 ASSERT_TRUE(path_len >= 0);
133 *exe_name = std::string(path, path_len);
134 }
135
RunTest(std::string test_name)136 void MallocHooksTest::RunTest(std::string test_name) {
137 std::vector<const char*> args;
138 args.push_back("--gtest_also_run_disabled_tests");
139 std::string filter_arg("--gtest_filter=" + test_name);
140 args.push_back(filter_arg.c_str());
141
142 ExecTestHelper test;
143 test.Run(
144 [&]() {
145 std::string exe_name;
146 GetExe(&exe_name);
147 args.insert(args.begin(), exe_name.c_str());
148 args.push_back(nullptr);
149 execv(args[0], reinterpret_cast<char* const*>(const_cast<char**>(args.data())));
150 exit(1);
151 },
152 0, nullptr);
153 }
154
test_malloc_hook(size_t size,const void * arg)155 void* MallocHooksTest::test_malloc_hook(size_t size, const void* arg) {
156 malloc_hook_called_ = true;
157 void_arg_ = arg;
158 return orig_malloc_hook_(size, arg);
159 }
160
test_realloc_hook(void * ptr,size_t size,const void * arg)161 void* MallocHooksTest::test_realloc_hook(void* ptr, size_t size, const void* arg) {
162 realloc_hook_called_ = true;
163 void_arg_ = arg;
164 return orig_realloc_hook_(ptr, size, arg);
165 }
166
test_memalign_hook(size_t alignment,size_t size,const void * arg)167 void* MallocHooksTest::test_memalign_hook(size_t alignment, size_t size, const void* arg) {
168 memalign_hook_called_ = true;
169 void_arg_ = arg;
170 return orig_memalign_hook_(alignment, size, arg);
171 }
172
test_free_hook(void * ptr,const void * arg)173 void MallocHooksTest::test_free_hook(void* ptr, const void* arg) {
174 free_hook_called_ = true;
175 void_arg_ = arg;
176 return orig_free_hook_(ptr, arg);
177 }
178
TEST_F(MallocHooksTest,other_malloc_functions)179 TEST_F(MallocHooksTest, other_malloc_functions) {
180 RunTest("*.DISABLED_other_malloc_functions");
181 }
182
183 // Call other intercepted functions to make sure there are no crashes.
TEST_F(MallocHooksTest,DISABLED_other_malloc_functions)184 TEST_F(MallocHooksTest, DISABLED_other_malloc_functions) {
185 struct mallinfo info = mallinfo();
186 EXPECT_NE(0U, info.uordblks);
187
188 EXPECT_EQ(0, mallopt(-1000, -1000));
189
190 void* ptr = malloc(1024);
191 EXPECT_LE(1024U, malloc_usable_size(ptr));
192 free(ptr);
193 }
194
TEST_F(MallocHooksTest,extended_functions)195 TEST_F(MallocHooksTest, extended_functions) {
196 RunTest("*.DISABLED_extended_functions");
197 }
198
TEST_F(MallocHooksTest,DISABLED_extended_functions)199 TEST_F(MallocHooksTest, DISABLED_extended_functions) {
200 uint8_t* info = nullptr;
201 size_t overall_size = 100;
202 size_t info_size = 200;
203 size_t total_memory = 300;
204 size_t backtrace_size = 400;
205 get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
206 EXPECT_EQ(nullptr, info);
207 EXPECT_EQ(0U, overall_size);
208 EXPECT_EQ(0U, info_size);
209 EXPECT_EQ(0U, total_memory);
210 EXPECT_EQ(0U, backtrace_size);
211
212 free_malloc_leak_info(info);
213
214 malloc_enable();
215 malloc_disable();
216
217 EXPECT_EQ(0, malloc_iterate(0, 0, nullptr, nullptr));
218
219 // Malloc hooks doesn't support any backtracing.
220 EXPECT_EQ(0, malloc_backtrace(nullptr, nullptr, 10));
221 }
222
TEST_F(MallocHooksTest,malloc_hook)223 TEST_F(MallocHooksTest, malloc_hook) {
224 RunTest("*.DISABLED_malloc_hook");
225 }
226
TEST_F(MallocHooksTest,DISABLED_malloc_hook)227 TEST_F(MallocHooksTest, DISABLED_malloc_hook) {
228 Init();
229 ASSERT_TRUE(__malloc_hook != nullptr);
230 __malloc_hook = test_malloc_hook;
231
232 void* ptr = malloc(1024);
233 ASSERT_TRUE(ptr != nullptr);
234 write(0, ptr, 0);
235 free(ptr);
236
237 EXPECT_TRUE(malloc_hook_called_) << "The malloc hook was not called.";
238 EXPECT_TRUE(void_arg_ != nullptr) << "The malloc hook was called with a nullptr.";
239 }
240
TEST_F(MallocHooksTest,free_hook)241 TEST_F(MallocHooksTest, free_hook) {
242 RunTest("*.DISABLED_free_hook");
243 }
244
TEST_F(MallocHooksTest,DISABLED_free_hook)245 TEST_F(MallocHooksTest, DISABLED_free_hook) {
246 Init();
247 ASSERT_TRUE(__free_hook != nullptr);
248 __free_hook = test_free_hook;
249
250 void* ptr = malloc(1024);
251 ASSERT_TRUE(ptr != nullptr);
252 free(ptr);
253 write(0, ptr, 0);
254
255 EXPECT_TRUE(free_hook_called_) << "The free hook was not called.";
256 EXPECT_TRUE(void_arg_ != nullptr) << "The free hook was called with a nullptr.";
257 }
258
TEST_F(MallocHooksTest,realloc_hook)259 TEST_F(MallocHooksTest, realloc_hook) {
260 RunTest("*.DISABLED_realloc_hook");
261 }
262
TEST_F(MallocHooksTest,DISABLED_realloc_hook)263 TEST_F(MallocHooksTest, DISABLED_realloc_hook) {
264 Init();
265 ASSERT_TRUE(__realloc_hook != nullptr);
266 __realloc_hook = test_realloc_hook;
267
268 void* ptr = malloc(1024);
269 ASSERT_TRUE(ptr != nullptr);
270 ptr = realloc(ptr, 2048);
271 free(ptr);
272 write(0, ptr, 0);
273
274 EXPECT_TRUE(realloc_hook_called_) << "The realloc hook was not called.";
275 EXPECT_TRUE(void_arg_ != nullptr) << "The realloc hook was called with a nullptr.";
276 }
277
TEST_F(MallocHooksTest,memalign_hook)278 TEST_F(MallocHooksTest, memalign_hook) {
279 RunTest("*.DISABLED_memalign_hook");
280 }
281
TEST_F(MallocHooksTest,DISABLED_memalign_hook)282 TEST_F(MallocHooksTest, DISABLED_memalign_hook) {
283 Init();
284 ASSERT_TRUE(__memalign_hook != nullptr);
285 __memalign_hook = test_memalign_hook;
286
287 void* ptr = memalign(32, 1024);
288 ASSERT_TRUE(ptr != nullptr);
289 free(ptr);
290 write(0, ptr, 0);
291
292 EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called.";
293 EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
294 }
295
TEST_F(MallocHooksTest,posix_memalign_hook)296 TEST_F(MallocHooksTest, posix_memalign_hook) {
297 RunTest("*.DISABLED_posix_memalign_hook");
298 }
299
TEST_F(MallocHooksTest,DISABLED_posix_memalign_hook)300 TEST_F(MallocHooksTest, DISABLED_posix_memalign_hook) {
301 Init();
302 ASSERT_TRUE(__memalign_hook != nullptr);
303 __memalign_hook = test_memalign_hook;
304
305 void* ptr;
306 ASSERT_EQ(0, posix_memalign(&ptr, 32, 1024));
307 ASSERT_TRUE(ptr != nullptr);
308 free(ptr);
309 write(0, ptr, 0);
310
311 EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called when running posix_memalign.";
312 EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
313 }
314
TEST_F(MallocHooksTest,posix_memalign_hook_error)315 TEST_F(MallocHooksTest, posix_memalign_hook_error) {
316 RunTest("*.DISABLED_posix_memalign_hook_error");
317 }
318
TEST_F(MallocHooksTest,DISABLED_posix_memalign_hook_error)319 TEST_F(MallocHooksTest, DISABLED_posix_memalign_hook_error) {
320 Init();
321 ASSERT_TRUE(__memalign_hook != nullptr);
322 __memalign_hook = test_memalign_hook;
323
324 void* ptr;
325 ASSERT_EQ(EINVAL, posix_memalign(&ptr, 11, 1024));
326 write(0, ptr, 0);
327
328 EXPECT_FALSE(memalign_hook_called_)
329 << "The memalign hook was called when running posix_memalign with an error.";
330 EXPECT_FALSE(void_arg_ != nullptr)
331 << "The memalign hook was called with a nullptr with an error.";
332 }
333
TEST_F(MallocHooksTest,aligned_alloc_hook)334 TEST_F(MallocHooksTest, aligned_alloc_hook) {
335 RunTest("*.DISABLED_aligned_alloc_hook");
336 }
337
TEST_F(MallocHooksTest,DISABLED_aligned_alloc_hook)338 TEST_F(MallocHooksTest, DISABLED_aligned_alloc_hook) {
339 Init();
340 ASSERT_TRUE(__memalign_hook != nullptr);
341 __memalign_hook = test_memalign_hook;
342
343 void* ptr = aligned_alloc(32, 1024);
344 ASSERT_TRUE(ptr != nullptr);
345 free(ptr);
346 write(0, ptr, 0);
347
348 EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called when running aligned_alloc.";
349 EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
350 }
351
TEST_F(MallocHooksTest,aligned_alloc_hook_error)352 TEST_F(MallocHooksTest, aligned_alloc_hook_error) {
353 RunTest("*.DISABLED_aligned_alloc_hook_error");
354 }
355
TEST_F(MallocHooksTest,DISABLED_aligned_alloc_hook_error)356 TEST_F(MallocHooksTest, DISABLED_aligned_alloc_hook_error) {
357 Init();
358 ASSERT_TRUE(__memalign_hook != nullptr);
359 __memalign_hook = test_memalign_hook;
360
361 void* ptr = aligned_alloc(11, 1024);
362 ASSERT_TRUE(ptr == nullptr);
363 EXPECT_EQ(EINVAL, errno);
364 write(0, ptr, 0);
365
366 EXPECT_FALSE(memalign_hook_called_)
367 << "The memalign hook was called when running aligned_alloc with an error.";
368 EXPECT_FALSE(void_arg_ != nullptr)
369 << "The memalign hook was called with a nullptr with an error.";
370 }
371
372 #if !defined(__LP64__)
TEST_F(MallocHooksTest,pvalloc_hook)373 TEST_F(MallocHooksTest, pvalloc_hook) {
374 RunTest("*.DISABLED_pvalloc_hook");
375 }
376
377 extern "C" void* pvalloc(size_t);
378
TEST_F(MallocHooksTest,DISABLED_pvalloc_hook)379 TEST_F(MallocHooksTest, DISABLED_pvalloc_hook) {
380 Init();
381 ASSERT_TRUE(__memalign_hook != nullptr);
382 __memalign_hook = test_memalign_hook;
383
384 void* ptr = pvalloc(1024);
385 ASSERT_TRUE(ptr != nullptr);
386 write(0, ptr, 0);
387 free(ptr);
388
389 EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called for pvalloc.";
390 EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
391 }
392
TEST_F(MallocHooksTest,valloc_hook)393 TEST_F(MallocHooksTest, valloc_hook) {
394 RunTest("*.DISABLED_valloc_hook");
395 }
396
397 extern "C" void* valloc(size_t);
398
TEST_F(MallocHooksTest,DISABLED_valloc_hook)399 TEST_F(MallocHooksTest, DISABLED_valloc_hook) {
400 Init();
401 ASSERT_TRUE(__memalign_hook != nullptr);
402 __memalign_hook = test_memalign_hook;
403
404 void* ptr = valloc(1024);
405 ASSERT_TRUE(ptr != nullptr);
406 write(0, ptr, 0);
407 free(ptr);
408
409 EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called for valloc.";
410 EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
411 }
412 #endif
413