Implement malloc hooks.
Use the malloc debug framework to implement the malloc debug hooks
since it can introduce a performance issue.
Also, modify the bionic/tests/utils.h slightly to dump an error message
when the exe failed.
Bug: 30561479
Test: Ran malloc hook unit tests.
Test: Ran malloc debug unit tests.
Test: Enabled malloc hooks and ran bionic unit tests and verified no
Test: unexpected failures.
Test: Enabled malloc debug and malloc hooks and verified malloc debug wins.
Test: Enabled malloc debug using env, property, and property with name
Test: still works.
Change-Id: Ib50046a0493c5c2050cf831befb812310bdcc249
(cherry picked from commit d6a1dc23796696f73f483943534d4c5c4b312d39)
diff --git a/libc/malloc_hooks/Android.bp b/libc/malloc_hooks/Android.bp
new file mode 100644
index 0000000..d4b5a2a
--- /dev/null
+++ b/libc/malloc_hooks/Android.bp
@@ -0,0 +1,66 @@
+// ==============================================================
+// libc_malloc_hooks.so
+// ==============================================================
+cc_library {
+ name: "libc_malloc_hooks",
+
+ srcs: [
+ "malloc_hooks.cpp",
+ ],
+
+ static_libs: [
+ "libasync_safe",
+ ],
+
+ multilib: {
+ lib32: {
+ version_script: "exported32.map",
+ },
+ lib64: {
+ version_script: "exported64.map",
+ },
+ },
+ include_dirs: ["bionic/libc"],
+
+ sanitize: {
+ never: true,
+ },
+ native_coverage: false,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-fno-stack-protector",
+ ],
+}
+
+// ==============================================================
+// Unit Tests
+// ==============================================================
+cc_test {
+ name: "malloc_hooks_unit_tests",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: [
+ "tests/malloc_hooks_tests.cpp",
+ ],
+
+ whole_static_libs: ["libc_malloc_hooks"],
+
+ shared_libs: ["libbase"],
+
+ local_include_dirs: ["tests"],
+ include_dirs: ["bionic/libc", "bionic"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libc/malloc_hooks/README.md b/libc/malloc_hooks/README.md
new file mode 100644
index 0000000..85b2769
--- /dev/null
+++ b/libc/malloc_hooks/README.md
@@ -0,0 +1,118 @@
+Malloc Hooks
+============
+
+Malloc hooks allows a program to intercept all allocation/free calls that
+happen during execution. It is only available in Android P and newer versions
+of the OS.
+
+There are two ways to enable these hooks, set a special system
+property, or set a special environment variable and run your app/program.
+
+When malloc hooks is enabled, it works by adding a shim layer that replaces
+the normal allocation calls. The replaced calls are:
+
+* `malloc`
+* `free`
+* `calloc`
+* `realloc`
+* `posix_memalign`
+* `memalign`
+* `aligned_alloc`
+* `malloc_usable_size`
+
+On 32 bit systems, these two deprecated functions are also replaced:
+
+* `pvalloc`
+* `valloc`
+
+These four hooks are defined in malloc.h:
+
+ void* (*volatile __malloc_hook)(size_t, const void*);
+ void* (*volatile __realloc_hook)(void*, size_t, const void*);
+ void (*volatile __free_hook)(void*, const void*);
+ void* (*volatile __memalign_hook)(size_t, size_t, const void*);
+
+When malloc is called and \_\_malloc\_hook has been set, then the hook
+function is called instead.
+
+When realloc is called and \_\_realloc\_hook has been set, then the hook
+function is called instead.
+
+When free is called and \_\_free\_hook has been set, then the hook
+function is called instead.
+
+When memalign is called and \_\_memalign\_hook has been set, then the hook
+function is called instead.
+
+For posix\_memalign, if \_\_memalign\_hook has been set, then the hook is
+called, but only if alignment is a power of 2.
+
+For aligned\_alloc, if \_\_memalign\_hook has been set, then the hook is
+called, but only if alignment is a power of 2.
+
+For calloc, if \_\_malloc\_hook has been set, then the hook function is
+called, then the allocated memory is set to zero.
+
+For the two deprecated functions pvalloc and valloc, if \_\_memalign\_hook
+has been set, then the hook is called with an appropriate alignment value.
+
+There is no hook for malloc\_usable\_size as of now.
+
+These hooks can be set at any time, but there is no thread safety, so
+the caller must guarantee that it does not depend on allocations/frees
+occurring at the same time.
+
+Implementation Details
+======================
+When malloc hooks is enabled, then the hook pointers are set to
+the current default allocation functions. It is expected that if an
+app does intercept the allocation/free calls, it will eventually call
+the original hook function to do allocations. If the app does not do this,
+it runs the risk of crashing whenever a malloc\_usable\_size call is made.
+
+Example Implementation
+======================
+Below is a simple implementation intercepting only malloc/calloc calls.
+
+ void* new_malloc_hook(size_t bytes, const char* arg) {
+ return orig_malloc_hook(bytes, arg);
+ }
+
+ void orig_malloc_hook = __malloc_hook;
+ __malloc_hook = new_malloc_hook;
+
+Enabling Examples
+=================
+
+### For platform developers
+
+Enable the hooks for all processes:
+
+ adb shell stop
+ adb shell setprop libc.debug.malloc.hooks 1
+ adb shell start
+
+Enable malloc debug using an environment variable:
+
+ adb shell
+ # export LIBC_HOOK_ENABLE=1
+ # ls
+
+Any process spawned from this shell will run with malloc hooks enabled.
+
+### For app developers
+
+Enable malloc hooks for a specific program/application:
+
+ adb shell setprop wrap.<APP> '"LIBC_HOOKS_ENABLE=1"'
+
+For example, to enable malloc hooks for the google search box:
+
+ adb shell setprop wrap.com.google.android.googlequicksearchbox '"LIBC_HOOKS_ENABLE=1 logwrapper"'
+ adb shell am force-stop com.google.android.googlequicksearchbox
+
+NOTE: On pre-O versions of the Android OS, property names had a length limit
+of 32. This meant that to create a wrap property with the name of the app, it
+was necessary to truncate the name to fit. On O, property names can be
+an order of magnitude larger, so there should be no need to truncate the name
+at all.
diff --git a/libc/malloc_hooks/exported32.map b/libc/malloc_hooks/exported32.map
new file mode 100644
index 0000000..2cf5b8c
--- /dev/null
+++ b/libc/malloc_hooks/exported32.map
@@ -0,0 +1,26 @@
+LIBC_MALLOC_HOOKS {
+ global:
+ hooks_aligned_alloc;
+ hooks_calloc;
+ hooks_finalize;
+ hooks_free;
+ hooks_free_malloc_leak_info;
+ hooks_get_malloc_leak_info;
+ hooks_initialize;
+ hooks_iterate;
+ hooks_mallinfo;
+ hooks_malloc;
+ hooks_malloc_backtrace;
+ hooks_malloc_disable;
+ hooks_malloc_enable;
+ hooks_malloc_usable_size;
+ hooks_mallopt;
+ hooks_memalign;
+ hooks_posix_memalign;
+ hooks_pvalloc;
+ hooks_realloc;
+ hooks_valloc;
+
+ local:
+ *;
+};
diff --git a/libc/malloc_hooks/exported64.map b/libc/malloc_hooks/exported64.map
new file mode 100644
index 0000000..5ebcf9f
--- /dev/null
+++ b/libc/malloc_hooks/exported64.map
@@ -0,0 +1,24 @@
+LIBC_MALLOC_HOOKS {
+ global:
+ hooks_aligned_alloc;
+ hooks_calloc;
+ hooks_finalize;
+ hooks_free;
+ hooks_free_malloc_leak_info;
+ hooks_get_malloc_leak_info;
+ hooks_initialize;
+ hooks_iterate;
+ hooks_mallinfo;
+ hooks_malloc;
+ hooks_malloc_backtrace;
+ hooks_malloc_disable;
+ hooks_malloc_enable;
+ hooks_malloc_usable_size;
+ hooks_mallopt;
+ hooks_memalign;
+ hooks_posix_memalign;
+ hooks_realloc;
+
+ local:
+ *;
+};
diff --git a/libc/malloc_hooks/malloc_hooks.cpp b/libc/malloc_hooks/malloc_hooks.cpp
new file mode 100644
index 0000000..662520c
--- /dev/null
+++ b/libc/malloc_hooks/malloc_hooks.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+#include <private/bionic_malloc_dispatch.h>
+
+// ------------------------------------------------------------------------
+// Global Data
+// ------------------------------------------------------------------------
+const MallocDispatch* g_dispatch;
+// ------------------------------------------------------------------------
+
+// ------------------------------------------------------------------------
+// Use C style prototypes for all exported functions. This makes it easy
+// to do dlsym lookups during libc initialization when hooks are enabled.
+// ------------------------------------------------------------------------
+__BEGIN_DECLS
+
+bool hooks_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child,
+ const char* options);
+void hooks_finalize();
+void hooks_get_malloc_leak_info(
+ uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory,
+ size_t* backtrace_size);
+ssize_t hooks_malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
+void hooks_free_malloc_leak_info(uint8_t* info);
+size_t hooks_malloc_usable_size(void* pointer);
+void* hooks_malloc(size_t size);
+void hooks_free(void* pointer);
+void* hooks_memalign(size_t alignment, size_t bytes);
+void* hooks_aligned_alloc(size_t alignment, size_t bytes);
+void* hooks_realloc(void* pointer, size_t bytes);
+void* hooks_calloc(size_t nmemb, size_t bytes);
+struct mallinfo hooks_mallinfo();
+int hooks_mallopt(int param, int value);
+int hooks_posix_memalign(void** memptr, size_t alignment, size_t size);
+int hooks_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+void hooks_malloc_disable();
+void hooks_malloc_enable();
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+void* hooks_pvalloc(size_t bytes);
+void* hooks_valloc(size_t size);
+#endif
+
+static void* default_malloc_hook(size_t bytes, const void*) {
+ return g_dispatch->malloc(bytes);
+}
+
+static void* default_realloc_hook(void* pointer, size_t bytes, const void*) {
+ return g_dispatch->realloc(pointer, bytes);
+}
+
+static void default_free_hook(void* pointer, const void*) {
+ g_dispatch->free(pointer);
+}
+
+static void* default_memalign_hook(size_t alignment, size_t bytes, const void*) {
+ return g_dispatch->memalign(alignment, bytes);
+}
+
+__END_DECLS
+// ------------------------------------------------------------------------
+
+bool hooks_initialize(const MallocDispatch* malloc_dispatch, int*, const char*) {
+ g_dispatch = malloc_dispatch;
+ __malloc_hook = default_malloc_hook;
+ __realloc_hook = default_realloc_hook;
+ __free_hook = default_free_hook;
+ __memalign_hook = default_memalign_hook;
+ return true;
+}
+
+void hooks_finalize() {
+}
+
+void hooks_get_malloc_leak_info(uint8_t** info, size_t* overall_size,
+ size_t* info_size, size_t* total_memory, size_t* backtrace_size) {
+ *info = nullptr;
+ *overall_size = 0;
+ *info_size = 0;
+ *total_memory = 0;
+ *backtrace_size = 0;
+}
+
+void hooks_free_malloc_leak_info(uint8_t*) {
+}
+
+size_t hooks_malloc_usable_size(void* pointer) {
+ return g_dispatch->malloc_usable_size(pointer);
+}
+
+void* hooks_malloc(size_t size) {
+ if (__malloc_hook != nullptr && __malloc_hook != default_malloc_hook) {
+ return __malloc_hook(size, __builtin_return_address(0));
+ }
+ return g_dispatch->malloc(size);
+}
+
+void hooks_free(void* pointer) {
+ if (__free_hook != nullptr && __free_hook != default_free_hook) {
+ return __free_hook(pointer, __builtin_return_address(0));
+ }
+ return g_dispatch->free(pointer);
+}
+
+void* hooks_memalign(size_t alignment, size_t bytes) {
+ if (__memalign_hook != nullptr && __memalign_hook != default_memalign_hook) {
+ return __memalign_hook(alignment, bytes, __builtin_return_address(0));
+ }
+ return g_dispatch->memalign(alignment, bytes);
+}
+
+void* hooks_realloc(void* pointer, size_t bytes) {
+ if (__realloc_hook != nullptr && __realloc_hook != default_realloc_hook) {
+ return __realloc_hook(pointer, bytes, __builtin_return_address(0));
+ }
+ return g_dispatch->realloc(pointer, bytes);
+}
+
+void* hooks_calloc(size_t nmemb, size_t bytes) {
+ if (__malloc_hook != nullptr && __malloc_hook != default_malloc_hook) {
+ size_t size;
+ if (__builtin_mul_overflow(nmemb, bytes, &size)) {
+ return nullptr;
+ }
+ void* ptr = __malloc_hook(size, __builtin_return_address(0));
+ if (ptr != nullptr) {
+ memset(ptr, 0, size);
+ }
+ return ptr;
+ }
+ return g_dispatch->calloc(nmemb, bytes);
+}
+
+struct mallinfo hooks_mallinfo() {
+ return g_dispatch->mallinfo();
+}
+
+int hooks_mallopt(int param, int value) {
+ return g_dispatch->mallopt(param, value);
+}
+
+void* hooks_aligned_alloc(size_t alignment, size_t size) {
+ if (__memalign_hook != nullptr && __memalign_hook != default_memalign_hook) {
+ if (!powerof2(alignment)) {
+ errno = EINVAL;
+ return nullptr;
+ }
+ void* ptr = __memalign_hook(alignment, size, __builtin_return_address(0));
+ if (ptr == nullptr) {
+ errno = ENOMEM;
+ }
+ return ptr;
+ }
+ return g_dispatch->aligned_alloc(alignment, size);
+}
+
+int hooks_posix_memalign(void** memptr, size_t alignment, size_t size) {
+ if (__memalign_hook != nullptr && __memalign_hook != default_memalign_hook) {
+ if (!powerof2(alignment)) {
+ return EINVAL;
+ }
+ *memptr = __memalign_hook(alignment, size, __builtin_return_address(0));
+ if (*memptr == nullptr) {
+ return ENOMEM;
+ }
+ return 0;
+ }
+ return g_dispatch->posix_memalign(memptr, alignment, size);
+}
+
+int hooks_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*) {
+ return 0;
+}
+
+void hooks_malloc_disable() {
+}
+
+void hooks_malloc_enable() {
+}
+
+ssize_t hooks_malloc_backtrace(void*, uintptr_t*, size_t) {
+ return 0;
+}
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+void* hooks_pvalloc(size_t bytes) {
+ size_t pagesize = getpagesize();
+ size_t size = __BIONIC_ALIGN(bytes, pagesize);
+ if (size < bytes) {
+ // Overflow
+ errno = ENOMEM;
+ return nullptr;
+ }
+ return hooks_memalign(pagesize, size);
+}
+
+void* hooks_valloc(size_t size) {
+ return hooks_memalign(getpagesize(), size);
+}
+#endif
diff --git a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp
new file mode 100644
index 0000000..0d23a6a
--- /dev/null
+++ b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <fcntl.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <private/bionic_malloc_dispatch.h>
+#include <tests/utils.h>
+
+__BEGIN_DECLS
+
+void get_malloc_leak_info(uint8_t**, size_t*, size_t*, size_t*, size_t*);
+void free_malloc_leak_info(uint8_t*);
+int malloc_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
+void malloc_enable();
+void malloc_disable();
+ssize_t malloc_backtrace(void*, uintptr_t*, size_t);
+
+__END_DECLS
+
+class MallocHooksTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_EQ(0, setenv("LIBC_HOOKS_ENABLE", "1", true));
+ initialized_ = false;
+ }
+
+ void TearDown() override {
+ if (initialized_) {
+ Reset();
+ }
+ ASSERT_EQ(0, unsetenv("LIBC_HOOKS_ENABLE"));
+ }
+
+ void Init() {
+ initialized_ = true;
+ malloc_hook_called_ = false;
+ free_hook_called_ = false;
+ realloc_hook_called_ = false;
+ memalign_hook_called_ = false;
+
+ void_arg_ = nullptr;
+
+ orig_malloc_hook_ = __malloc_hook;
+ orig_free_hook_ = __free_hook;
+ orig_realloc_hook_ = __realloc_hook;
+ orig_memalign_hook_ = __memalign_hook;
+ }
+
+ void Reset() {
+ __malloc_hook = orig_malloc_hook_;
+ __free_hook = orig_free_hook_;
+ __realloc_hook = orig_realloc_hook_;
+ __memalign_hook = orig_memalign_hook_;
+ }
+
+ void Exec(std::vector<const char*> args);
+ void RunTest(std::string test_name);
+
+ public:
+ bool initialized_;
+
+ int fd_;
+ std::string raw_output_;
+ pid_t pid_;
+
+ static bool malloc_hook_called_;
+ static bool free_hook_called_;
+ static bool realloc_hook_called_;
+ static bool memalign_hook_called_;
+ static const void* void_arg_;
+
+ static void* (*orig_malloc_hook_)(size_t, const void*);
+ static void (*orig_free_hook_)(void*, const void*);
+ static void* (*orig_realloc_hook_)(void*, size_t, const void*);
+ static void* (*orig_memalign_hook_)(size_t, size_t, const void*);
+
+ static void* test_malloc_hook(size_t, const void*);
+ static void* test_realloc_hook(void* ptr, size_t, const void*);
+ static void* test_memalign_hook(size_t, size_t, const void*);
+ static void test_free_hook(void*, const void*);
+};
+
+bool MallocHooksTest::malloc_hook_called_;
+bool MallocHooksTest::free_hook_called_;
+bool MallocHooksTest::realloc_hook_called_;
+bool MallocHooksTest::memalign_hook_called_;
+const void* MallocHooksTest::void_arg_;
+
+void* (*MallocHooksTest::orig_malloc_hook_)(size_t, const void*);
+void (*MallocHooksTest::orig_free_hook_)(void*, const void*);
+void* (*MallocHooksTest::orig_realloc_hook_)(void*, size_t, const void*);
+void* (*MallocHooksTest::orig_memalign_hook_)(size_t, size_t, const void*);
+
+static void GetExe(std::string* exe_name) {
+ char path[PATH_MAX];
+ ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path));
+ ASSERT_TRUE(path_len >= 0);
+ *exe_name = std::string(path, path_len);
+}
+
+void MallocHooksTest::RunTest(std::string test_name) {
+ std::vector<const char*> args;
+ args.push_back("--gtest_also_run_disabled_tests");
+ std::string filter_arg("--gtest_filter=" + test_name);
+ args.push_back(filter_arg.c_str());
+
+ ExecTestHelper test;
+ test.Run(
+ [&]() {
+ std::string exe_name;
+ GetExe(&exe_name);
+ args.insert(args.begin(), exe_name.c_str());
+ args.push_back(nullptr);
+ execv(args[0], reinterpret_cast<char* const*>(const_cast<char**>(args.data())));
+ exit(1);
+ },
+ 0, nullptr);
+}
+
+void* MallocHooksTest::test_malloc_hook(size_t size, const void* arg) {
+ malloc_hook_called_ = true;
+ void_arg_ = arg;
+ return orig_malloc_hook_(size, arg);
+}
+
+void* MallocHooksTest::test_realloc_hook(void* ptr, size_t size, const void* arg) {
+ realloc_hook_called_ = true;
+ void_arg_ = arg;
+ return orig_realloc_hook_(ptr, size, arg);
+}
+
+void* MallocHooksTest::test_memalign_hook(size_t alignment, size_t size, const void* arg) {
+ memalign_hook_called_ = true;
+ void_arg_ = arg;
+ return orig_memalign_hook_(alignment, size, arg);
+}
+
+void MallocHooksTest::test_free_hook(void* ptr, const void* arg) {
+ free_hook_called_ = true;
+ void_arg_ = arg;
+ return orig_free_hook_(ptr, arg);
+}
+
+TEST_F(MallocHooksTest, other_malloc_functions) {
+ RunTest("*.DISABLED_other_malloc_functions");
+}
+
+// Call other intercepted functions to make sure there are no crashes.
+TEST_F(MallocHooksTest, DISABLED_other_malloc_functions) {
+ struct mallinfo info = mallinfo();
+ EXPECT_NE(0U, info.uordblks);
+
+ EXPECT_EQ(0, mallopt(-1000, -1000));
+
+ void* ptr = malloc(1024);
+ EXPECT_LE(1024U, malloc_usable_size(ptr));
+ free(ptr);
+}
+
+TEST_F(MallocHooksTest, extended_functions) {
+ RunTest("*.DISABLED_extended_functions");
+}
+
+TEST_F(MallocHooksTest, DISABLED_extended_functions) {
+ uint8_t* info = nullptr;
+ size_t overall_size = 100;
+ size_t info_size = 200;
+ size_t total_memory = 300;
+ size_t backtrace_size = 400;
+ get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+ EXPECT_EQ(nullptr, info);
+ EXPECT_EQ(0U, overall_size);
+ EXPECT_EQ(0U, info_size);
+ EXPECT_EQ(0U, total_memory);
+ EXPECT_EQ(0U, backtrace_size);
+
+ free_malloc_leak_info(info);
+
+ malloc_enable();
+ malloc_disable();
+
+ EXPECT_EQ(0, malloc_iterate(0, 0, nullptr, nullptr));
+
+ // Malloc hooks doesn't support any backtracing.
+ EXPECT_EQ(0, malloc_backtrace(nullptr, nullptr, 10));
+}
+
+TEST_F(MallocHooksTest, malloc_hook) {
+ RunTest("*.DISABLED_malloc_hook");
+}
+
+TEST_F(MallocHooksTest, DISABLED_malloc_hook) {
+ Init();
+ ASSERT_TRUE(__malloc_hook != nullptr);
+ __malloc_hook = test_malloc_hook;
+
+ void* ptr = malloc(1024);
+ ASSERT_TRUE(ptr != nullptr);
+ write(0, ptr, 0);
+ free(ptr);
+
+ EXPECT_TRUE(malloc_hook_called_) << "The malloc hook was not called.";
+ EXPECT_TRUE(void_arg_ != nullptr) << "The malloc hook was called with a nullptr.";
+}
+
+TEST_F(MallocHooksTest, free_hook) {
+ RunTest("*.DISABLED_free_hook");
+}
+
+TEST_F(MallocHooksTest, DISABLED_free_hook) {
+ Init();
+ ASSERT_TRUE(__free_hook != nullptr);
+ __free_hook = test_free_hook;
+
+ void* ptr = malloc(1024);
+ ASSERT_TRUE(ptr != nullptr);
+ free(ptr);
+ write(0, ptr, 0);
+
+ EXPECT_TRUE(free_hook_called_) << "The free hook was not called.";
+ EXPECT_TRUE(void_arg_ != nullptr) << "The free hook was called with a nullptr.";
+}
+
+TEST_F(MallocHooksTest, realloc_hook) {
+ RunTest("*.DISABLED_realloc_hook");
+}
+
+TEST_F(MallocHooksTest, DISABLED_realloc_hook) {
+ Init();
+ ASSERT_TRUE(__realloc_hook != nullptr);
+ __realloc_hook = test_realloc_hook;
+
+ void* ptr = malloc(1024);
+ ASSERT_TRUE(ptr != nullptr);
+ ptr = realloc(ptr, 2048);
+ free(ptr);
+ write(0, ptr, 0);
+
+ EXPECT_TRUE(realloc_hook_called_) << "The realloc hook was not called.";
+ EXPECT_TRUE(void_arg_ != nullptr) << "The realloc hook was called with a nullptr.";
+}
+
+TEST_F(MallocHooksTest, memalign_hook) {
+ RunTest("*.DISABLED_memalign_hook");
+}
+
+TEST_F(MallocHooksTest, DISABLED_memalign_hook) {
+ Init();
+ ASSERT_TRUE(__memalign_hook != nullptr);
+ __memalign_hook = test_memalign_hook;
+
+ void* ptr = memalign(32, 1024);
+ ASSERT_TRUE(ptr != nullptr);
+ free(ptr);
+ write(0, ptr, 0);
+
+ EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called.";
+ EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
+}
+
+TEST_F(MallocHooksTest, posix_memalign_hook) {
+ RunTest("*.DISABLED_posix_memalign_hook");
+}
+
+TEST_F(MallocHooksTest, DISABLED_posix_memalign_hook) {
+ Init();
+ ASSERT_TRUE(__memalign_hook != nullptr);
+ __memalign_hook = test_memalign_hook;
+
+ void* ptr;
+ ASSERT_EQ(0, posix_memalign(&ptr, 32, 1024));
+ ASSERT_TRUE(ptr != nullptr);
+ free(ptr);
+ write(0, ptr, 0);
+
+ EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called when running posix_memalign.";
+ EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
+}
+
+TEST_F(MallocHooksTest, posix_memalign_hook_error) {
+ RunTest("*.DISABLED_posix_memalign_hook_error");
+}
+
+TEST_F(MallocHooksTest, DISABLED_posix_memalign_hook_error) {
+ Init();
+ ASSERT_TRUE(__memalign_hook != nullptr);
+ __memalign_hook = test_memalign_hook;
+
+ void* ptr;
+ ASSERT_EQ(EINVAL, posix_memalign(&ptr, 11, 1024));
+ write(0, ptr, 0);
+
+ EXPECT_FALSE(memalign_hook_called_)
+ << "The memalign hook was called when running posix_memalign with an error.";
+ EXPECT_FALSE(void_arg_ != nullptr)
+ << "The memalign hook was called with a nullptr with an error.";
+}
+
+TEST_F(MallocHooksTest, aligned_alloc_hook) {
+ RunTest("*.DISABLED_aligned_alloc_hook");
+}
+
+TEST_F(MallocHooksTest, DISABLED_aligned_alloc_hook) {
+ Init();
+ ASSERT_TRUE(__memalign_hook != nullptr);
+ __memalign_hook = test_memalign_hook;
+
+ void* ptr = aligned_alloc(32, 1024);
+ ASSERT_TRUE(ptr != nullptr);
+ free(ptr);
+ write(0, ptr, 0);
+
+ EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called when running aligned_alloc.";
+ EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
+}
+
+TEST_F(MallocHooksTest, aligned_alloc_hook_error) {
+ RunTest("*.DISABLED_aligned_alloc_hook_error");
+}
+
+TEST_F(MallocHooksTest, DISABLED_aligned_alloc_hook_error) {
+ Init();
+ ASSERT_TRUE(__memalign_hook != nullptr);
+ __memalign_hook = test_memalign_hook;
+
+ void* ptr = aligned_alloc(11, 1024);
+ ASSERT_TRUE(ptr == nullptr);
+ EXPECT_EQ(EINVAL, errno);
+ write(0, ptr, 0);
+
+ EXPECT_FALSE(memalign_hook_called_)
+ << "The memalign hook was called when running aligned_alloc with an error.";
+ EXPECT_FALSE(void_arg_ != nullptr)
+ << "The memalign hook was called with a nullptr with an error.";
+}
+
+#if !defined(__LP64__)
+TEST_F(MallocHooksTest, pvalloc_hook) {
+ RunTest("*.DISABLED_pvalloc_hook");
+}
+
+extern "C" void* pvalloc(size_t);
+
+TEST_F(MallocHooksTest, DISABLED_pvalloc_hook) {
+ Init();
+ ASSERT_TRUE(__memalign_hook != nullptr);
+ __memalign_hook = test_memalign_hook;
+
+ void* ptr = pvalloc(1024);
+ ASSERT_TRUE(ptr != nullptr);
+ write(0, ptr, 0);
+ free(ptr);
+
+ EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called for pvalloc.";
+ EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
+}
+
+TEST_F(MallocHooksTest, valloc_hook) {
+ RunTest("*.DISABLED_valloc_hook");
+}
+
+extern "C" void* valloc(size_t);
+
+TEST_F(MallocHooksTest, DISABLED_valloc_hook) {
+ Init();
+ ASSERT_TRUE(__memalign_hook != nullptr);
+ __memalign_hook = test_memalign_hook;
+
+ void* ptr = valloc(1024);
+ ASSERT_TRUE(ptr != nullptr);
+ write(0, ptr, 0);
+ free(ptr);
+
+ EXPECT_TRUE(memalign_hook_called_) << "The memalign hook was not called for valloc.";
+ EXPECT_TRUE(void_arg_ != nullptr) << "The memalign hook was called with a nullptr.";
+}
+#endif