diff options
Diffstat (limited to 'libc/malloc_hooks')
-rw-r--r-- | libc/malloc_hooks/Android.bp | 66 | ||||
-rw-r--r-- | libc/malloc_hooks/README.md | 118 | ||||
-rw-r--r-- | libc/malloc_hooks/exported32.map | 26 | ||||
-rw-r--r-- | libc/malloc_hooks/exported64.map | 24 | ||||
-rw-r--r-- | libc/malloc_hooks/malloc_hooks.cpp | 234 | ||||
-rw-r--r-- | libc/malloc_hooks/tests/malloc_hooks_tests.cpp | 412 |
6 files changed, 880 insertions, 0 deletions
diff --git a/libc/malloc_hooks/Android.bp b/libc/malloc_hooks/Android.bp new file mode 100644 index 000000000..d4b5a2aa1 --- /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 000000000..85b276958 --- /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 000000000..2cf5b8c5c --- /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 000000000..5ebcf9ff7 --- /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 000000000..662520c15 --- /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 000000000..0d23a6a8f --- /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 |