diff options
Diffstat (limited to 'libc/malloc_debug/tests/malloc_debug_unit_tests.cpp')
-rw-r--r-- | libc/malloc_debug/tests/malloc_debug_unit_tests.cpp | 1391 |
1 files changed, 1391 insertions, 0 deletions
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp new file mode 100644 index 000000000..0e8ca68ee --- /dev/null +++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp @@ -0,0 +1,1391 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <malloc.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/types.h> +#include <unistd.h> + +#include <algorithm> +#include <vector> +#include <utility> + +#include <gtest/gtest.h> + +#include <android-base/stringprintf.h> + +#include <private/bionic_macros.h> +#include <private/bionic_malloc_dispatch.h> + +#include "malloc_debug.h" + +#include "log_fake.h" +#include "backtrace_fake.h" + +__BEGIN_DECLS + +int property_set(const char*, const char*); +bool debug_initialize(const MallocDispatch*, int*); +void debug_finalize(); + +void* debug_malloc(size_t); +void debug_free(void*); +void* debug_calloc(size_t, size_t); +void* debug_realloc(void*, size_t); +int debug_posix_memalign(void**, size_t, size_t); +void* debug_memalign(size_t, size_t); +size_t debug_malloc_usable_size(void*); +void debug_get_malloc_leak_info(uint8_t**, size_t*, size_t*, size_t*, size_t*); +void debug_free_malloc_leak_info(uint8_t*); + +struct mallinfo debug_mallinfo(); + +#if defined(HAVE_DEPRECATED_MALLOC_FUNCS) +void* debug_pvalloc(size_t); +void* debug_valloc(size_t); +#endif + +__END_DECLS + +constexpr char DIVIDER[] = + "6 malloc_debug *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"; + +constexpr uint32_t TRACK_HEADER = 0x2; +constexpr uint32_t BACKTRACE_HEADER = 0x4; + +static size_t get_tag_offset(uint32_t flags = 0, size_t backtrace_frames = 0) { + size_t offset = BIONIC_ALIGN(sizeof(Header), sizeof(uintptr_t)); + if (flags & TRACK_HEADER) { + offset += BIONIC_ALIGN(sizeof(TrackHeader), sizeof(uintptr_t)); + } + if (flags & BACKTRACE_HEADER) { + offset += BIONIC_ALIGN(sizeof(BacktraceHeader) + sizeof(uintptr_t) * backtrace_frames - 1, sizeof(uintptr_t)); + offset += BIONIC_ALIGN(sizeof(BacktraceHeader) + sizeof(uintptr_t) * backtrace_frames - 1, sizeof(uintptr_t)); + } + return offset; +} + +class MallocDebugTest : public ::testing::Test { + protected: + void SetUp() override { + initialized = false; + resetLogs(); + backtrace_fake_clear_all(); + } + + void TearDown() override { + if (initialized) { + debug_finalize(); + } + } + + void Init(const char* property_value) { + property_set("libc.debug.malloc.options", property_value); + zygote = 0; + ASSERT_TRUE(debug_initialize(&dispatch, &zygote)); + initialized = true; + } + + bool initialized; + + int zygote; + + static MallocDispatch dispatch; +}; + +MallocDispatch MallocDebugTest::dispatch = { + .calloc = calloc, + .free = free, + .mallinfo = mallinfo, + .malloc = malloc, + .malloc_usable_size = malloc_usable_size, + .memalign = memalign, + .posix_memalign = posix_memalign, + .realloc = realloc, +}; + +void VerifyAllocCalls() { + size_t alloc_size = 1024; + + // Verify debug_malloc. + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(alloc_size)); + ASSERT_TRUE(pointer != nullptr); + for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { + ASSERT_EQ(0xeb, pointer[i]); + } + debug_free(pointer); + + // Verify debug_calloc. + pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, alloc_size)); + ASSERT_TRUE(pointer != nullptr); + for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { + ASSERT_EQ(0, pointer[i]) << "Failed at byte " << i; + } + debug_free(pointer); + + pointer = reinterpret_cast<uint8_t*>(debug_memalign(128, alloc_size)); + ASSERT_TRUE(pointer != nullptr); + for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { + ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i; + } + debug_free(pointer); + + pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, alloc_size)); + ASSERT_TRUE(pointer != nullptr); + for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { + ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i; + } + memset(pointer, 0xff, alloc_size); + // Increase the size, verify the extra length is initialized to 0xeb, + // but the rest is 0xff. + pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, alloc_size * 2)); + ASSERT_TRUE(pointer != nullptr); + for (size_t i = 0; i < alloc_size; i++) { + ASSERT_EQ(0xff, pointer[i]) << "Failed at byte " << i; + } + for (size_t i = alloc_size; i < debug_malloc_usable_size(pointer); i++) { + ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i; + } + memset(pointer, 0xff, debug_malloc_usable_size(pointer)); + // Shrink the size and verify nothing changes. + pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, alloc_size)); + ASSERT_TRUE(pointer != nullptr); + for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { + ASSERT_EQ(0xff, pointer[i]) << "Failed at byte " << i; + } + // This should free the pointer. + pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0)); + ASSERT_TRUE(pointer == nullptr); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, fill_generic) { + Init("fill"); + VerifyAllocCalls(); +} + +TEST_F(MallocDebugTest, fill_on_alloc_generic) { + Init("fill_on_alloc"); + VerifyAllocCalls(); +} + +TEST_F(MallocDebugTest, fill_on_alloc_partial) { + Init("fill_on_alloc=25"); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + for (size_t i = 0; i < 25; i++) { + ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i; + } + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, fill_on_free) { + Init("fill_on_free free_track"); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + size_t usable_size = debug_malloc_usable_size(pointer); + memset(pointer, 0, usable_size); + debug_free(pointer); + + for (size_t i = 0; i < usable_size; i++) { + ASSERT_EQ(0xef, pointer[i]) << "Failed at byte " << i; + } + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, fill_on_free_partial) { + Init("fill_on_free=30 free_track"); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + size_t usable_size = debug_malloc_usable_size(pointer); + memset(pointer, 0, usable_size); + debug_free(pointer); + + for (size_t i = 0; i < 30; i++) { + ASSERT_EQ(0xef, pointer[i]) << "Failed to fill on free at byte " << i; + } + for (size_t i = 30; i < usable_size; i++) { + ASSERT_EQ(0, pointer[i]) << "Filled too much on byte " << i; + } + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, free_track_partial) { + Init("fill_on_free=30 free_track"); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + size_t usable_size = debug_malloc_usable_size(pointer); + memset(pointer, 0, usable_size); + debug_free(pointer); + + for (size_t i = 0; i < 30; i++) { + ASSERT_EQ(0xef, pointer[i]) << "Failed to fill on free at byte " << i; + } + for (size_t i = 30; i < usable_size; i++) { + ASSERT_EQ(0, pointer[i]) << "Filled too much on byte " << i; + } + + debug_finalize(); + initialized = false; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, all_options) { + Init("guard backtrace fill expand_alloc free_track leak_track"); + VerifyAllocCalls(); +} + +TEST_F(MallocDebugTest, expand_alloc) { + Init("expand_alloc=1024"); + + void* pointer = debug_malloc(10); + ASSERT_TRUE(pointer != nullptr); + ASSERT_LE(1034U, debug_malloc_usable_size(pointer)); + debug_free(pointer); + + pointer = debug_calloc(1, 20); + ASSERT_TRUE(pointer != nullptr); + ASSERT_LE(1044U, debug_malloc_usable_size(pointer)); + debug_free(pointer); + + pointer = debug_memalign(128, 15); + ASSERT_TRUE(pointer != nullptr); + ASSERT_LE(1039U, debug_malloc_usable_size(pointer)); + debug_free(pointer); + + pointer = debug_realloc(nullptr, 30); + ASSERT_TRUE(pointer != nullptr); + ASSERT_LE(1054U, debug_malloc_usable_size(pointer)); + pointer = debug_realloc(pointer, 100); + ASSERT_LE(1124U, debug_malloc_usable_size(pointer)); + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, front_guard) { + Init("front_guard=32"); + + // Create a buffer for doing comparisons. + std::vector<uint8_t> buffer(32); + memset(buffer.data(), 0xaa, buffer.size()); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0); + memset(pointer, 0xff, 100); + debug_free(pointer); + + // Loop through a bunch alignments. + for (size_t alignment = 1; alignment <= 256; alignment++) { + pointer = reinterpret_cast<uint8_t*>(debug_memalign(alignment, 100)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0); + size_t alignment_mask = alignment - 1; + if (!powerof2(alignment)) { + alignment_mask = BIONIC_ROUND_UP_POWER_OF_2(alignment) - 1; + } + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(pointer) & alignment_mask); + memset(pointer, 0xff, 100); + debug_free(pointer); + } + + pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, 100)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0); + for (size_t i = 0; i < 100; i++) { + ASSERT_EQ(0, pointer[i]) << "debug_calloc non-zero byte at " << i; + } + debug_free(pointer); + + pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, 100)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0); + memset(pointer, 0xff, 100); + pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 200)); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0); + memset(pointer, 0xff, 200); + pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0)); + ASSERT_TRUE(pointer == nullptr); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, realloc_memalign_memory) { + Init("rear_guard"); + + void* pointer = debug_memalign(1024, 100); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 100); + + pointer = debug_realloc(pointer, 1024); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(1024U, debug_malloc_usable_size(pointer)); + memset(pointer, 0, 1024); + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, front_guard_corrupted) { + Init("front_guard=32"); + + backtrace_fake_add(std::vector<uintptr_t> {0x1, 0x2, 0x3}); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + pointer[-32] = 0x00; + pointer[-15] = 0x02; + debug_free(pointer); + + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED FRONT GUARD\n", pointer); + expected_log += "6 malloc_debug pointer[-32] = 0x00 (expected 0xaa)\n"; + expected_log += "6 malloc_debug pointer[-15] = 0x02 (expected 0xaa)\n"; + expected_log += "6 malloc_debug Backtrace at time of failure:\n"; + expected_log += "6 malloc_debug #00 pc 0x1\n"; + expected_log += "6 malloc_debug #01 pc 0x2\n"; + expected_log += "6 malloc_debug #02 pc 0x3\n"; + expected_log += DIVIDER; + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, rear_guard) { + Init("rear_guard=32"); + + // Create a buffer for doing comparisons. + std::vector<uint8_t> buffer(32); + memset(buffer.data(), 0xbb, buffer.size()); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0); + memset(pointer, 0xff, 100); + debug_free(pointer); + + // Loop through a bunch alignments. + for (size_t alignment = 1; alignment <= 256; alignment++) { + pointer = reinterpret_cast<uint8_t*>(debug_memalign(alignment, 100)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0); + size_t alignment_mask = alignment - 1; + if (!powerof2(alignment)) { + alignment_mask = BIONIC_ROUND_UP_POWER_OF_2(alignment) - 1; + } + ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(pointer) & alignment_mask) + << "Failed at alignment " << alignment << " mask " << alignment_mask; + memset(pointer, 0xff, 100); + debug_free(pointer); + } + + pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, 100)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0); + for (size_t i = 0; i < 100; i++) { + ASSERT_EQ(0, pointer[i]) << "debug_calloc non-zero byte at " << i; + } + debug_free(pointer); + + pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, 100)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0); + memset(pointer, 0xff, 100); + pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 200)); + ASSERT_TRUE(memcmp(buffer.data(), &pointer[200], buffer.size()) == 0); + for (size_t i = 0; i < 100; i++) { + ASSERT_EQ(0xff, pointer[i]) << "debug_realloc not copied byte at " << i; + } + memset(pointer, 0xff, 200); + pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0)); + ASSERT_TRUE(pointer == nullptr); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, rear_guard_corrupted) { + Init("rear_guard=32"); + + backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300}); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + pointer[130] = 0xbf; + pointer[131] = 0x00; + debug_free(pointer); + + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED REAR GUARD\n", pointer); + expected_log += "6 malloc_debug pointer[130] = 0xbf (expected 0xbb)\n"; + expected_log += "6 malloc_debug pointer[131] = 0x00 (expected 0xbb)\n"; + expected_log += "6 malloc_debug Backtrace at time of failure:\n"; + expected_log += "6 malloc_debug #00 pc 0x100\n"; + expected_log += "6 malloc_debug #01 pc 0x200\n"; + expected_log += "6 malloc_debug #02 pc 0x300\n"; + expected_log += DIVIDER; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, rear_guard_corrupted_after_realloc_shrink) { + Init("rear_guard=32"); + + backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300}); + + void* pointer = debug_malloc(200); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 200); + + uint8_t* pointer_shrink = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 100)); + pointer_shrink[130] = 0xbf; + pointer_shrink[131] = 0x00; + debug_free(pointer); + + // When shrinking sizes, the same pointer should be returned. + ASSERT_EQ(pointer, pointer_shrink); + + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED REAR GUARD\n", pointer); + expected_log += "6 malloc_debug pointer[130] = 0xbf (expected 0xbb)\n"; + expected_log += "6 malloc_debug pointer[131] = 0x00 (expected 0xbb)\n"; + expected_log += "6 malloc_debug Backtrace at time of failure:\n"; + expected_log += "6 malloc_debug #00 pc 0x100\n"; + expected_log += "6 malloc_debug #01 pc 0x200\n"; + expected_log += "6 malloc_debug #02 pc 0x300\n"; + expected_log += DIVIDER; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, tag_corrupted) { + Init("rear_guard=32"); + + backtrace_fake_add(std::vector<uintptr_t> {0xa, 0xb, 0xc}); + + backtrace_fake_add(std::vector<uintptr_t> {0xaa, 0xbb, 0xcc}); + + backtrace_fake_add(std::vector<uintptr_t> {0xaaa, 0xbbb, 0xccc}); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + uint8_t saved = pointer[-get_tag_offset()]; + pointer[-get_tag_offset()] = 0x00; + ASSERT_EQ(0U, debug_malloc_usable_size(pointer)); + ASSERT_TRUE(debug_realloc(pointer, 200) == nullptr); + debug_free(pointer); + + // Fix the pointer and really free it. + pointer[-get_tag_offset()] = saved; + debug_free(pointer); + + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (malloc_usable_size)\n", + pointer); + expected_log += "6 malloc_debug Backtrace at time of failure:\n"; + expected_log += "6 malloc_debug #00 pc 0xa\n"; + expected_log += "6 malloc_debug #01 pc 0xb\n"; + expected_log += "6 malloc_debug #02 pc 0xc\n"; + expected_log += DIVIDER; + + expected_log += DIVIDER; + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (realloc)\n", + pointer); + expected_log += "6 malloc_debug Backtrace at time of failure:\n"; + expected_log += "6 malloc_debug #00 pc 0xaa\n"; + expected_log += "6 malloc_debug #01 pc 0xbb\n"; + expected_log += "6 malloc_debug #02 pc 0xcc\n"; + expected_log += DIVIDER; + + expected_log += DIVIDER; + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (free)\n", + pointer); + expected_log += "6 malloc_debug Backtrace at time of failure:\n"; + expected_log += "6 malloc_debug #00 pc 0xaaa\n"; + expected_log += "6 malloc_debug #01 pc 0xbbb\n"; + expected_log += "6 malloc_debug #02 pc 0xccc\n"; + expected_log += DIVIDER; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, leak_track_no_frees) { + Init("leak_track"); + + void* pointer1 = debug_malloc(200); + ASSERT_TRUE(pointer1 != nullptr); + memset(pointer1, 0, 200); + + void* pointer2 = debug_malloc(128); + ASSERT_TRUE(pointer2 != nullptr); + memset(pointer2, 0, 128); + + void* pointer3 = debug_malloc(1024); + ASSERT_TRUE(pointer3 != nullptr); + memset(pointer3, 0, 1024); + + debug_finalize(); + initialized = false; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log = android::base::StringPrintf( + "6 malloc_debug +++ malloc_testing leaked block of size 1024 at %p (leak 1 of 3)\n", + pointer3); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ malloc_testing leaked block of size 200 at %p (leak 2 of 3)\n", + pointer1); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ malloc_testing leaked block of size 128 at %p (leak 3 of 3)\n", + pointer2); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, leak_track_no_frees_with_backtrace) { + Init("leak_track backtrace"); + + backtrace_fake_add(std::vector<uintptr_t> {0x1000, 0x2000, 0x3000}); + + void* pointer1 = debug_malloc(100); + ASSERT_TRUE(pointer1 != nullptr); + memset(pointer1, 0, 100); + + backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000, 0xc000, 0xd000}); + + void* pointer2 = debug_malloc(128); + ASSERT_TRUE(pointer2 != nullptr); + memset(pointer2, 0, 128); + + backtrace_fake_add(std::vector<uintptr_t> {0xfe000, 0xde000, 0xce000, 0xbe000, 0xae000}); + + void* pointer3 = debug_malloc(1024); + ASSERT_TRUE(pointer3 != nullptr); + memset(pointer3, 0, 1024); + + debug_finalize(); + initialized = false; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log = android::base::StringPrintf( + "6 malloc_debug +++ malloc_testing leaked block of size 1024 at %p (leak 1 of 3)\n", + pointer3); + expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; + expected_log += "6 malloc_debug #00 pc 0xfe000\n"; + expected_log += "6 malloc_debug #01 pc 0xde000\n"; + expected_log += "6 malloc_debug #02 pc 0xce000\n"; + expected_log += "6 malloc_debug #03 pc 0xbe000\n"; + expected_log += "6 malloc_debug #04 pc 0xae000\n"; + + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ malloc_testing leaked block of size 128 at %p (leak 2 of 3)\n", + pointer2); + expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; + expected_log += "6 malloc_debug #00 pc 0xa000\n"; + expected_log += "6 malloc_debug #01 pc 0xb000\n"; + expected_log += "6 malloc_debug #02 pc 0xc000\n"; + expected_log += "6 malloc_debug #03 pc 0xd000\n"; + + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ malloc_testing leaked block of size 100 at %p (leak 3 of 3)\n", + pointer1); + expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; + expected_log += "6 malloc_debug #00 pc 0x1000\n"; + expected_log += "6 malloc_debug #01 pc 0x2000\n"; + expected_log += "6 malloc_debug #02 pc 0x3000\n"; + + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, leak_track_frees) { + Init("leak_track"); + + void* pointer1 = debug_malloc(390); + ASSERT_TRUE(pointer1 != nullptr); + memset(pointer1, 0, 390); + debug_free(pointer1); + + pointer1 = debug_malloc(100); + ASSERT_TRUE(pointer1 != nullptr); + memset(pointer1, 0, 100); + + void* pointer2 = debug_malloc(250); + ASSERT_TRUE(pointer2 != nullptr); + memset(pointer2, 0, 250); + debug_free(pointer2); + + pointer2 = debug_malloc(450); + ASSERT_TRUE(pointer2 != nullptr); + memset(pointer2, 0, 450); + + void* pointer3 = debug_malloc(999); + ASSERT_TRUE(pointer3 != nullptr); + memset(pointer3, 0, 999); + debug_free(pointer2); + + debug_finalize(); + initialized = false; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log = android::base::StringPrintf( + "6 malloc_debug +++ malloc_testing leaked block of size 999 at %p (leak 1 of 2)\n", + pointer3); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ malloc_testing leaked block of size 100 at %p (leak 2 of 2)\n", + pointer1); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, free_track) { + Init("free_track=5"); + + void* pointers[10]; + for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) { + pointers[i] = debug_malloc(100 + i); + ASSERT_TRUE(pointers[i] != nullptr); + memset(pointers[i], 0, 100 + i); + debug_free(pointers[i]); + } + + // Large allocations (> 4096) to verify large allocation checks. + void* pointer = debug_malloc(8192); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 8192); + debug_free(pointer); + + pointer = debug_malloc(9000); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 9000); + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, free_track_use_after_free) { + Init("free_track=5"); + + uint8_t* pointers[5]; + for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) { + pointers[i] = reinterpret_cast<uint8_t*>(debug_malloc(100 + i)); + ASSERT_TRUE(pointers[i] != nullptr); + memset(pointers[i], 0, 100 + i); + debug_free(pointers[i]); + } + + // Stomp on the data. + pointers[0][20] = 0xaf; + pointers[0][99] = 0x12; + + pointers[3][3] = 0x34; + + // Large allocations (> 4096) to verify large allocation checks. + uint8_t* pointer1_large = reinterpret_cast<uint8_t*>(debug_malloc(8192)); + ASSERT_TRUE(pointer1_large != nullptr); + memset(pointer1_large, 0, 8192); + debug_free(pointer1_large); + + pointer1_large[4095] = 0x90; + pointer1_large[4100] = 0x56; + pointer1_large[8191] = 0x89; + + uint8_t* pointer2_large = reinterpret_cast<uint8_t*>(debug_malloc(9000)); + ASSERT_TRUE(pointer2_large != nullptr); + memset(pointer2_large, 0, 9000); + debug_free(pointer2_large); + + pointer2_large[8200] = 0x78; + + // Do a bunch of alloc and free to verify the above frees are checked. + for (size_t i = 0; i < 10; i++) { + void* flush_pointer = debug_malloc(100+i); + ASSERT_TRUE(flush_pointer != nullptr); + memset(flush_pointer, 0, 100 + i); + debug_free(flush_pointer); + } + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointers[0]); + expected_log += "6 malloc_debug pointer[20] = 0xaf (expected 0xef)\n"; + expected_log += "6 malloc_debug pointer[99] = 0x12 (expected 0xef)\n"; + expected_log += DIVIDER; + expected_log += DIVIDER; + expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointers[3]); + expected_log += "6 malloc_debug pointer[3] = 0x34 (expected 0xef)\n"; + expected_log += DIVIDER; + expected_log += DIVIDER; + expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer1_large); + expected_log += "6 malloc_debug pointer[4095] = 0x90 (expected 0xef)\n"; + expected_log += "6 malloc_debug pointer[4100] = 0x56 (expected 0xef)\n"; + expected_log += "6 malloc_debug pointer[8191] = 0x89 (expected 0xef)\n"; + expected_log += DIVIDER; + expected_log += DIVIDER; + expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer2_large); + expected_log += "6 malloc_debug pointer[8200] = 0x78 (expected 0xef)\n"; + expected_log += DIVIDER; + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, free_track_use_after_free_finalize) { + Init("free_track=100"); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 100); + debug_free(pointer); + + pointer[56] = 0x91; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); + + debug_finalize(); + initialized = false; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer); + expected_log += "6 malloc_debug pointer[56] = 0x91 (expected 0xef)\n"; + expected_log += DIVIDER; + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, free_track_use_after_free_with_backtrace) { + Init("free_track=100 backtrace"); + + // Alloc backtrace. + backtrace_fake_add(std::vector<uintptr_t> {0xf0, 0xe, 0xd}); + // Free backtrace. + backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc}); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(200)); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 200); + debug_free(pointer); + + pointer[101] = 0xab; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); + + debug_finalize(); + initialized = false; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer); + expected_log += "6 malloc_debug pointer[101] = 0xab (expected 0xef)\n"; + expected_log += "6 malloc_debug Backtrace at time of free:\n"; + expected_log += "6 malloc_debug #00 pc 0xfa\n"; + expected_log += "6 malloc_debug #01 pc 0xeb\n"; + expected_log += "6 malloc_debug #02 pc 0xdc\n"; + expected_log += DIVIDER; + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, get_malloc_leak_info_invalid) { + Init("fill"); + + uint8_t* info; + size_t overall_size; + size_t info_size; + size_t total_memory; + size_t backtrace_size; + + std::string expected_log("6 malloc_debug get_malloc_leak_info: At least one invalid parameter.\n"); + + debug_get_malloc_leak_info(nullptr, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); + + resetLogs(); + debug_get_malloc_leak_info(&info, nullptr, &info_size, &total_memory, &backtrace_size); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); + + resetLogs(); + debug_get_malloc_leak_info(&info, &overall_size, nullptr, &total_memory, &backtrace_size); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); + + resetLogs(); + debug_get_malloc_leak_info(&info, &overall_size, &info_size, nullptr, &backtrace_size); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); + + resetLogs(); + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, nullptr); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, get_malloc_leak_info_not_enabled) { + Init("fill"); + + uint8_t* info; + size_t overall_size; + size_t info_size; + size_t total_memory; + size_t backtrace_size; + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + std::string expected_log( + "6 malloc_debug get_malloc_leak_info: Allocations not being tracked, to enable " + "set the option 'backtrace'.\n"); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +struct InfoEntry { + size_t size; + size_t num_frames; + uintptr_t frames[0]; +} __attribute__((packed)); + +TEST_F(MallocDebugTest, get_malloc_leak_info_empty) { + Init("backtrace"); + + uint8_t* info; + size_t overall_size; + size_t info_size; + size_t total_memory; + size_t backtrace_size; + + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_TRUE(info == nullptr); + ASSERT_EQ(0U, overall_size); + ASSERT_EQ(0U, info_size); + ASSERT_EQ(0U, total_memory); + ASSERT_EQ(0U, backtrace_size); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, get_malloc_leak_info_single) { + Init("backtrace"); + + // Create the expected info buffer. + size_t individual_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t); + std::vector<uint8_t> expected_info(individual_size); + memset(expected_info.data(), 0, individual_size); + + InfoEntry* entry = reinterpret_cast<InfoEntry*>(expected_info.data()); + entry->size = 200; + entry->num_frames = 3; + entry->frames[0] = 0xf; + entry->frames[1] = 0xe; + entry->frames[2] = 0xd; + + backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd}); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(entry->size)); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, entry->size); + + uint8_t* info; + size_t overall_size; + size_t info_size; + size_t total_memory; + size_t backtrace_size; + + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_TRUE(info != nullptr); + ASSERT_EQ(individual_size, overall_size); + ASSERT_EQ(individual_size, info_size); + ASSERT_EQ(200U, total_memory); + ASSERT_EQ(16U, backtrace_size); + ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0); + + debug_free_malloc_leak_info(info); + + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, get_malloc_leak_info_multi) { + Init("backtrace=16"); + + // Create the expected info buffer. + size_t individual_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t); + std::vector<uint8_t> expected_info(individual_size * 3); + memset(expected_info.data(), 0, individual_size * 3); + + InfoEntry* entry0 = reinterpret_cast<InfoEntry*>(expected_info.data()); + InfoEntry* entry1 = reinterpret_cast<InfoEntry*>( + reinterpret_cast<uintptr_t>(entry0) + individual_size); + InfoEntry* entry2 = reinterpret_cast<InfoEntry*>( + reinterpret_cast<uintptr_t>(entry1) + individual_size); + + // These values will be in the reverse order that we create. + entry2->size = 500; + entry2->num_frames = 4; + entry2->frames[0] = 0xf; + entry2->frames[1] = 0xe; + entry2->frames[2] = 0xd; + entry2->frames[3] = 0xc; + + backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc}); + + uint8_t* pointers[3]; + + pointers[0] = reinterpret_cast<uint8_t*>(debug_malloc(entry2->size)); + ASSERT_TRUE(pointers[0] != nullptr); + memset(pointers[0], 0, entry2->size); + + entry1->size = 4100; + entry1->num_frames = 16; + for (size_t i = 0; i < 16; i++) { + entry1->frames[i] = 0xbc000 + i; + } + + backtrace_fake_add( + std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002, 0xbc003, 0xbc004, 0xbc005, + 0xbc006, 0xbc007, 0xbc008, 0xbc009, 0xbc00a, 0xbc00b, + 0xbc00c, 0xbc00d, 0xbc00e, 0xbc00f, 0xffff}); + + pointers[1] = reinterpret_cast<uint8_t*>(debug_malloc(entry1->size)); + ASSERT_TRUE(pointers[1] != nullptr); + memset(pointers[1], 0, entry1->size); + + entry0->size = 9000; + entry0->num_frames = 1; + + entry0->frames[0] = 0x104; + backtrace_fake_add(std::vector<uintptr_t> {0x104}); + + pointers[2] = reinterpret_cast<uint8_t*>(debug_malloc(entry0->size)); + ASSERT_TRUE(pointers[2] != nullptr); + memset(pointers[2], 0, entry0->size); + + uint8_t* info; + size_t overall_size; + size_t info_size; + size_t total_memory; + size_t backtrace_size; + + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_TRUE(info != nullptr); + ASSERT_EQ(individual_size * 3, overall_size); + ASSERT_EQ(individual_size, info_size); + ASSERT_EQ(500U + 4100U + 9000U, total_memory); + ASSERT_EQ(16U, backtrace_size); + ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0); + + debug_free_malloc_leak_info(info); + + debug_free(pointers[0]); + debug_free(pointers[1]); + debug_free(pointers[2]); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, get_malloc_leak_info_multi_skip_empty_backtrace) { + Init("backtrace=16"); + + // Create the expected info buffer. + size_t individual_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t); + std::vector<uint8_t> expected_info(individual_size * 2); + memset(expected_info.data(), 0, individual_size * 2); + + InfoEntry* entry0 = reinterpret_cast<InfoEntry*>(expected_info.data()); + InfoEntry* entry1 = reinterpret_cast<InfoEntry*>( + reinterpret_cast<uintptr_t>(entry0) + individual_size); + + // These values will be in the reverse order that we create. + entry1->size = 500; + entry1->num_frames = 4; + entry1->frames[0] = 0xf; + entry1->frames[1] = 0xe; + entry1->frames[2] = 0xd; + entry1->frames[3] = 0xc; + + backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc}); + + uint8_t* pointers[3]; + + pointers[0] = reinterpret_cast<uint8_t*>(debug_malloc(entry1->size)); + ASSERT_TRUE(pointers[0] != nullptr); + memset(pointers[0], 0, entry1->size); + + entry0->size = 4100; + entry0->num_frames = 16; + for (size_t i = 0; i < 16; i++) { + entry0->frames[i] = 0xbc000 + i; + } + + backtrace_fake_add( + std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002, 0xbc003, 0xbc004, 0xbc005, + 0xbc006, 0xbc007, 0xbc008, 0xbc009, 0xbc00a, 0xbc00b, + 0xbc00c, 0xbc00d, 0xbc00e, 0xbc00f, 0xffff}); + + pointers[1] = reinterpret_cast<uint8_t*>(debug_malloc(entry0->size)); + ASSERT_TRUE(pointers[1] != nullptr); + memset(pointers[1], 0, entry0->size); + + pointers[2] = reinterpret_cast<uint8_t*>(debug_malloc(10000)); + ASSERT_TRUE(pointers[2] != nullptr); + memset(pointers[2], 0, 10000); + + uint8_t* info; + size_t overall_size; + size_t info_size; + size_t total_memory; + size_t backtrace_size; + + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_TRUE(info != nullptr); + ASSERT_EQ(individual_size * 2, overall_size); + ASSERT_EQ(individual_size, info_size); + ASSERT_EQ(500U + 4100U, total_memory); + ASSERT_EQ(16U, backtrace_size); + ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0); + + debug_free_malloc_leak_info(info); + + debug_free(pointers[0]); + debug_free(pointers[1]); + debug_free(pointers[2]); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, realloc_usable_size) { + Init("front_guard"); + + // Verify that if the usable size > size of alloc, that realloc + // copies the bytes in the usable size not just the size. + // This assumes that an allocation of size 1 returns usable size > 1. + // If this isn't true, this test is not going to do anything. + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(1)); + ASSERT_TRUE(pointer != nullptr); + size_t usable_size = debug_malloc_usable_size(pointer); + memset(pointer, 0xaa, usable_size); + pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, usable_size + 10)); + ASSERT_TRUE(pointer != nullptr); + ASSERT_LE(usable_size + 10, debug_malloc_usable_size(pointer)); + for (size_t i = 0; i < usable_size; i++) { + ASSERT_EQ(0xaa, pointer[i]) << "Failed compare at byte " << i; + } + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, backtrace_enable_on_signal) { + Init("backtrace_enable_on_signal=20"); + + size_t individual_size = 2 * sizeof(size_t) + 20 * sizeof(uintptr_t); + + backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); + backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300, 0x400}); + backtrace_fake_add(std::vector<uintptr_t> {0x500, 0xa00, 0xb00}); + + // First allocation should not actually attempt to get the backtrace. + void* pointer = debug_malloc(10); + ASSERT_TRUE(pointer != nullptr); + + uint8_t* info; + size_t overall_size; + size_t info_size; + size_t total_memory; + size_t backtrace_size; + + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_TRUE(info == nullptr); + ASSERT_EQ(0U, overall_size); + ASSERT_EQ(0U, info_size); + ASSERT_EQ(0U, total_memory); + ASSERT_EQ(0U, backtrace_size); + debug_free(pointer); + + debug_free_malloc_leak_info(info); + + // Send the signal to enable. + ASSERT_TRUE(kill(getpid(), SIGRTMIN + 10) == 0); + sleep(1); + + pointer = debug_malloc(100); + ASSERT_TRUE(pointer != nullptr); + + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_TRUE(info != nullptr); + ASSERT_EQ(individual_size, overall_size); + ASSERT_EQ(individual_size, info_size); + ASSERT_EQ(100U, total_memory); + ASSERT_EQ(20U, backtrace_size); + uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]); + ASSERT_EQ(0xbc000U, ips[0]); + ASSERT_EQ(0xecd00U, ips[1]); + ASSERT_EQ(0x12000U, ips[2]); + for (size_t i = 3; i < 20; i++) { + ASSERT_EQ(0U, ips[i]); + } + + debug_free(pointer); + + debug_free_malloc_leak_info(info); + + // Send the signal to disable. + ASSERT_TRUE(kill(getpid(), SIGRTMIN + 10) == 0); + sleep(1); + + pointer = debug_malloc(200); + ASSERT_TRUE(pointer != nullptr); + + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_TRUE(info == nullptr); + ASSERT_EQ(0U, overall_size); + ASSERT_EQ(0U, info_size); + ASSERT_EQ(0U, total_memory); + ASSERT_EQ(0U, backtrace_size); + + debug_free(pointer); + + debug_free_malloc_leak_info(info); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log = android::base::StringPrintf( + "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to enable backtracing.\n", + SIGRTMIN + 10, getpid()); + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, overflow) { + Init("guard fill_on_free"); + + void* pointer = debug_malloc(SIZE_MAX); + ASSERT_TRUE(pointer == nullptr); + ASSERT_EQ(ENOMEM, errno); + + pointer = debug_calloc(1, SIZE_MAX); + ASSERT_TRUE(pointer == nullptr); + ASSERT_EQ(ENOMEM, errno); + + pointer = debug_calloc(SIZE_MAX, 1); + ASSERT_TRUE(pointer == nullptr); + ASSERT_EQ(ENOMEM, errno); + + pointer = debug_calloc(SIZE_MAX/100, 100); + ASSERT_TRUE(pointer == nullptr); + ASSERT_EQ(ENOMEM, errno); + + pointer = debug_calloc(100, SIZE_MAX/100); + ASSERT_TRUE(pointer == nullptr); + ASSERT_EQ(ENOMEM, errno); + + pointer = debug_realloc(nullptr, SIZE_MAX); + ASSERT_TRUE(pointer == nullptr); + ASSERT_EQ(ENOMEM, errno); + + pointer = debug_malloc(100); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0xd0, 100); + + void* realloc_pointer = debug_realloc(pointer, SIZE_MAX); + ASSERT_TRUE(realloc_pointer == nullptr); + // Verify the pointer was not freed. + for (size_t i = 0; i < 100; i++) { + ASSERT_EQ(0xd0, reinterpret_cast<uint8_t*>(pointer)[i]) << "Failed checking byte " << i; + } + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +static void VerifyZygoteSet(size_t memory_bytes) { + size_t expected_info_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t); + std::vector<uint8_t> expected_info(expected_info_size); + memset(expected_info.data(), 0, expected_info_size); + InfoEntry* entry = reinterpret_cast<InfoEntry*>(expected_info.data()); + entry->size = memory_bytes | (1U << 31); + entry->num_frames = 1; + entry->frames[0] = 0x1; + + uint8_t* info; + size_t overall_size; + size_t info_size; + size_t total_memory; + size_t backtrace_size; + + debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); + ASSERT_EQ(expected_info_size, overall_size); + ASSERT_EQ(expected_info_size, info_size); + ASSERT_EQ(memory_bytes, total_memory); + ASSERT_EQ(16U, backtrace_size); + ASSERT_TRUE(memcmp(info, expected_info.data(), expected_info_size) == 0); + + debug_free_malloc_leak_info(info); +} + +TEST_F(MallocDebugTest, zygote_set) { + // Set all of the options. + Init("guard fill backtrace leak_track free_track=2"); + + zygote = 1; + + backtrace_fake_add(std::vector<uintptr_t> {0x1}); + + void* pointer = debug_malloc(100); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); + memset(pointer, 0, 100); + VerifyZygoteSet(100); + debug_free(pointer); + + backtrace_fake_add(std::vector<uintptr_t> {0x1}); + pointer = debug_calloc(10, 20); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(200U, debug_malloc_usable_size(pointer)); + VerifyZygoteSet(200); + debug_free(pointer); + + backtrace_fake_add(std::vector<uintptr_t> {0x1}); + pointer = debug_memalign(128, 300); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(300U, debug_malloc_usable_size(pointer)); + memset(pointer, 0, 300); + VerifyZygoteSet(300); + debug_free(pointer); + + backtrace_fake_add(std::vector<uintptr_t> {0x1}); + pointer = debug_malloc(500); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(500U, debug_malloc_usable_size(pointer)); + memset(pointer, 0, 500); + VerifyZygoteSet(500); + + backtrace_fake_add(std::vector<uintptr_t> {0x1}); + pointer = debug_realloc(pointer, 300); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(300U, debug_malloc_usable_size(pointer)); + VerifyZygoteSet(300); + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, max_size) { + Init("guard"); + + void* pointer = debug_malloc(1U << 31); + ASSERT_TRUE(pointer == nullptr); + + pointer = debug_calloc(1, 1U << 31); + ASSERT_TRUE(pointer == nullptr); + + pointer = debug_calloc(1U << 31, 1); + ASSERT_TRUE(pointer == nullptr); + + pointer = debug_memalign(16, 1U << 31); + ASSERT_TRUE(pointer == nullptr); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, debug_mallinfo) { + Init("guard"); + + void* pointer = debug_malloc(150); + ASSERT_TRUE(pointer != nullptr); + + struct mallinfo mi = debug_mallinfo(); + EXPECT_NE(0U, mi.uordblks); + + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, debug_posix_memalign) { + Init("guard"); + + void* pointer; + ASSERT_EQ(0, debug_posix_memalign(&pointer, 32, 300)); + ASSERT_TRUE(pointer != nullptr); + debug_free(pointer); + + ASSERT_EQ(EINVAL, debug_posix_memalign(&pointer, 11, 300)); + + ASSERT_EQ(ENOMEM, debug_posix_memalign(&pointer, 16, SIZE_MAX)); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +#if defined(HAVE_DEPRECATED_MALLOC_FUNCS) +TEST_F(MallocDebugTest, debug_pvalloc) { + Init("guard"); + + size_t pagesize = getpagesize(); + void* pointer = debug_pvalloc(1); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(pagesize, debug_malloc_usable_size(pointer)); + uintptr_t value = reinterpret_cast<uintptr_t>(pointer) & (pagesize - 1); + ASSERT_EQ(0U, value); + debug_free(pointer); +} + +TEST_F(MallocDebugTest, debug_valloc) { + Init("guard"); + + size_t pagesize = getpagesize(); + void* pointer = debug_valloc(100); + ASSERT_TRUE(pointer != nullptr); + ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); + uintptr_t value = reinterpret_cast<uintptr_t>(pointer) & (pagesize - 1); + ASSERT_EQ(0U, value); + debug_free(pointer); +} +#endif |