diff options
author | Christopher Ferris <cferris@google.com> | 2016-01-28 18:35:05 -0800 |
---|---|---|
committer | Christopher Ferris <cferris@google.com> | 2016-01-29 15:19:22 -0800 |
commit | 7993b80f894db20af4d1d154221c42fea6171a3d (patch) | |
tree | fc99d36000a8f0853224d75296d96509a730ee05 | |
parent | 50647711ebaf2360aca05cc94a2fdf431c1a153e (diff) | |
download | android_bionic-7993b80f894db20af4d1d154221c42fea6171a3d.tar.gz android_bionic-7993b80f894db20af4d1d154221c42fea6171a3d.tar.bz2 android_bionic-7993b80f894db20af4d1d154221c42fea6171a3d.zip |
Add better free tracking.
Included in this change:
- Change the tag when a pointer is freed so it's easy to detect if
an already freed pointer is being used.
- Move the free backtrace out of the header. This backtrace is only
used under only some circumstances, so no need to allocate space
in all headers for it.
- Add new option free_track_backtrace_num_frames to specify how many
frames to record when the free occurs. This removes the dependency
on the backtrace option to get backtraces.
Bug: 26739265
Change-Id: I76f5209507dcf46af67ada162a7cb2bf282116f2
-rw-r--r-- | libc/malloc_debug/BacktraceData.cpp | 4 | ||||
-rw-r--r-- | libc/malloc_debug/BacktraceData.h | 2 | ||||
-rw-r--r-- | libc/malloc_debug/Config.cpp | 15 | ||||
-rw-r--r-- | libc/malloc_debug/Config.h | 1 | ||||
-rw-r--r-- | libc/malloc_debug/DebugData.h | 5 | ||||
-rw-r--r-- | libc/malloc_debug/FreeTrackData.cpp | 45 | ||||
-rw-r--r-- | libc/malloc_debug/FreeTrackData.h | 6 | ||||
-rw-r--r-- | libc/malloc_debug/backtrace.cpp | 2 | ||||
-rw-r--r-- | libc/malloc_debug/backtrace.h | 2 | ||||
-rw-r--r-- | libc/malloc_debug/malloc_debug.cpp | 23 | ||||
-rw-r--r-- | libc/malloc_debug/malloc_debug.h | 2 | ||||
-rw-r--r-- | libc/malloc_debug/tests/backtrace_fake.cpp | 2 | ||||
-rw-r--r-- | libc/malloc_debug/tests/malloc_debug_config_tests.cpp | 61 | ||||
-rw-r--r-- | libc/malloc_debug/tests/malloc_debug_unit_tests.cpp | 85 |
14 files changed, 208 insertions, 47 deletions
diff --git a/libc/malloc_debug/BacktraceData.cpp b/libc/malloc_debug/BacktraceData.cpp index 9f39068da..61267f086 100644 --- a/libc/malloc_debug/BacktraceData.cpp +++ b/libc/malloc_debug/BacktraceData.cpp @@ -42,11 +42,9 @@ #include "malloc_debug.h" BacktraceData::BacktraceData(const Config& config, size_t* offset) { - size_t hdr_len = sizeof(BacktraceHeader) + sizeof(uintptr_t) * config.backtrace_frames - 1; + size_t hdr_len = sizeof(BacktraceHeader) + sizeof(uintptr_t) * config.backtrace_frames; alloc_offset_ = *offset; *offset += BIONIC_ALIGN(hdr_len, sizeof(uintptr_t)); - free_offset_ = *offset; - *offset += BIONIC_ALIGN(hdr_len, sizeof(uintptr_t)); } static BacktraceData* g_backtrace_data = nullptr; diff --git a/libc/malloc_debug/BacktraceData.h b/libc/malloc_debug/BacktraceData.h index 10daec78c..842e37236 100644 --- a/libc/malloc_debug/BacktraceData.h +++ b/libc/malloc_debug/BacktraceData.h @@ -44,14 +44,12 @@ class BacktraceData { bool Initialize(const Config& config); inline size_t alloc_offset() { return alloc_offset_; } - inline size_t free_offset() { return free_offset_; } bool enabled() { return enabled_; } void set_enabled(bool enabled) { enabled_ = enabled; } private: size_t alloc_offset_ = 0; - size_t free_offset_ = 0; volatile bool enabled_ = false; diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp index 224bb08ed..032c1fc93 100644 --- a/libc/malloc_debug/Config.cpp +++ b/libc/malloc_debug/Config.cpp @@ -195,9 +195,17 @@ void PropertyParser::LogUsage() { error_log(" Instead, keep XX of these allocations around and then verify"); error_log(" that they have not been modified when the total number of freed"); error_log(" allocations exceeds the XX amount. When the program terminates,"); - error_log(" the rest of these allocations are verified."); + error_log(" the rest of these allocations are verified. When this option is"); + error_log(" enabled, it automatically records the backtrace at the time of the free."); error_log(" The default is to record 100 allocations."); error_log(""); + error_log(" free_track_backtrace_num_frames[=XX]"); + error_log(" This option only has meaning if free_track is set. This indicates"); + error_log(" how many backtrace frames to capture when an allocation is freed."); + error_log(" If XX is set, that is the number of frames to capture. If XX"); + error_log(" is set to zero, then no backtrace will be captured."); + error_log(" The default is to record 16 frames."); + error_log(""); error_log(" leak_track"); error_log(" Enable the leak tracking of memory allocations."); } @@ -245,6 +253,7 @@ bool Config::SetFromProperties() { front_guard_value = PropertyParser::DEFAULT_FRONT_GUARD_VALUE; rear_guard_value = PropertyParser::DEFAULT_REAR_GUARD_VALUE; backtrace_signal = SIGRTMIN + 10; + free_track_backtrace_num_frames = 16; // Parse the options are of the format: // option_name or option_name=XX @@ -286,6 +295,10 @@ bool Config::SetFromProperties() { // fill on free. Feature("free_track", 100, 1, 16384, FREE_TRACK | FILL_ON_FREE, &this->free_track_allocations, nullptr, false), + // Number of backtrace frames to keep when free_track is enabled. If this + // value is set to zero, no backtrace will be kept. + Feature("free_track_backtrace_num_frames", 16, 0, 256, 0, + &this->free_track_backtrace_num_frames, nullptr, false), // Enable printing leaked allocations. Feature("leak_track", 0, 0, 0, LEAK_TRACK | TRACK_ALLOCS, nullptr, nullptr, false), diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h index 4b91e2b76..d2cc56d46 100644 --- a/libc/malloc_debug/Config.h +++ b/libc/malloc_debug/Config.h @@ -61,6 +61,7 @@ struct Config { size_t expand_alloc_bytes = 0; size_t free_track_allocations = 0; + size_t free_track_backtrace_num_frames = 0; uint64_t options = 0; uint8_t fill_alloc_value; diff --git a/libc/malloc_debug/DebugData.h b/libc/malloc_debug/DebugData.h index e023c51c7..40978dbe3 100644 --- a/libc/malloc_debug/DebugData.h +++ b/libc/malloc_debug/DebugData.h @@ -67,11 +67,6 @@ class DebugData { return reinterpret_cast<BacktraceHeader*>(value + backtrace->alloc_offset()); } - BacktraceHeader* GetFreeBacktrace(const Header* header) { - uintptr_t value = reinterpret_cast<uintptr_t>(header); - return reinterpret_cast<BacktraceHeader*>(value + backtrace->free_offset()); - } - uint8_t* GetFrontGuard(const Header* header) { uintptr_t value = reinterpret_cast<uintptr_t>(header); return reinterpret_cast<uint8_t*>(value + front_guard->offset()); diff --git a/libc/malloc_debug/FreeTrackData.cpp b/libc/malloc_debug/FreeTrackData.cpp index 3466861c7..3ac54bf3d 100644 --- a/libc/malloc_debug/FreeTrackData.cpp +++ b/libc/malloc_debug/FreeTrackData.cpp @@ -36,7 +36,8 @@ #include "FreeTrackData.h" #include "malloc_debug.h" -FreeTrackData::FreeTrackData(const Config& config) { +FreeTrackData::FreeTrackData(const Config& config) + : backtrace_num_frames_(config.free_track_backtrace_num_frames) { cmp_mem_.resize(4096); memset(cmp_mem_.data(), config.fill_free_value, cmp_mem_.size()); } @@ -53,18 +54,19 @@ void FreeTrackData::LogFreeError(DebugData& debug, const Header* header, error_log(" pointer[%zu] = 0x%02x (expected 0x%02x)", i, pointer[i], fill_free_value); } } - if (debug.config().options & BACKTRACE) { - BacktraceHeader* back_header = debug.GetFreeBacktrace(header); - if (back_header->num_frames > 0) { - error_log("Backtrace at time of free:"); - backtrace_log(&back_header->frames[0], back_header->num_frames); - } + auto back_iter = backtraces_.find(header); + if (back_iter != backtraces_.end()) { + const BacktraceHeader* back_header = back_iter->second; + error_log("Backtrace at time of free:"); + backtrace_log(&back_header->frames[0], back_header->num_frames); } error_log(LOG_DIVIDER); } void FreeTrackData::VerifyAndFree(DebugData& debug, const Header* header, const void* pointer) { + ScopedDisableDebugCalls disable; + const uint8_t* memory = reinterpret_cast<const uint8_t*>(pointer); size_t bytes = header->usable_size; bytes = (bytes < debug.config().fill_on_free_bytes) ? bytes : debug.config().fill_on_free_bytes; @@ -77,6 +79,11 @@ void FreeTrackData::VerifyAndFree(DebugData& debug, const Header* header, bytes -= bytes_to_cmp; memory = &memory[bytes_to_cmp]; } + auto back_iter = backtraces_.find(header); + if (back_iter != backtraces_.end()) { + g_dispatch->free(reinterpret_cast<void*>(back_iter->second)); + backtraces_.erase(header); + } g_dispatch->free(header->orig_pointer); } @@ -86,10 +93,20 @@ void FreeTrackData::Add(DebugData& debug, const Header* header) { pthread_mutex_lock(&mutex_); if (list_.size() == debug.config().free_track_allocations) { - VerifyAndFree(debug, list_.back(), debug.GetPointer(list_.back())); + const Header* old_header = list_.back(); + VerifyAndFree(debug, old_header, debug.GetPointer(old_header)); list_.pop_back(); } + // Only log the free backtrace if we are using the free track feature. + if (backtrace_num_frames_ > 0) { + BacktraceHeader* back_header = reinterpret_cast<BacktraceHeader*>( + g_dispatch->malloc(sizeof(BacktraceHeader) + backtrace_num_frames_ * sizeof(uintptr_t))); + if (back_header) { + back_header->num_frames = backtrace_get(&back_header->frames[0], backtrace_num_frames_); + backtraces_[header] = back_header; + } + } list_.push_front(header); pthread_mutex_unlock(&mutex_); @@ -104,3 +121,15 @@ void FreeTrackData::VerifyAll(DebugData& debug) { } list_.clear(); } + +void FreeTrackData::LogBacktrace(const Header* header) { + ScopedDisableDebugCalls disable; + + auto back_iter = backtraces_.find(header); + if (back_iter == backtraces_.end()) { + return; + } + + error_log("Backtrace of original free:"); + backtrace_log(&back_iter->second->frames[0], back_iter->second->num_frames); +} diff --git a/libc/malloc_debug/FreeTrackData.h b/libc/malloc_debug/FreeTrackData.h index 5888a0ed7..804b5a6db 100644 --- a/libc/malloc_debug/FreeTrackData.h +++ b/libc/malloc_debug/FreeTrackData.h @@ -33,6 +33,7 @@ #include <pthread.h> #include <deque> +#include <unordered_map> #include <vector> #include <private/bionic_macros.h> @@ -41,6 +42,7 @@ struct Header; class DebugData; struct Config; +struct BacktraceHeader; class FreeTrackData { public: @@ -51,6 +53,8 @@ class FreeTrackData { void VerifyAll(DebugData& debug); + void LogBacktrace(const Header* header); + private: void LogFreeError(DebugData& debug, const Header* header, const uint8_t* pointer); void VerifyAndFree(DebugData& debug, const Header* header, const void* pointer); @@ -58,6 +62,8 @@ class FreeTrackData { pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER; std::deque<const Header*> list_; std::vector<uint8_t> cmp_mem_; + std::unordered_map<const Header*, BacktraceHeader*> backtraces_; + size_t backtrace_num_frames_; DISALLOW_COPY_AND_ASSIGN(FreeTrackData); }; diff --git a/libc/malloc_debug/backtrace.cpp b/libc/malloc_debug/backtrace.cpp index 00290fda6..8549dc49b 100644 --- a/libc/malloc_debug/backtrace.cpp +++ b/libc/malloc_debug/backtrace.cpp @@ -143,7 +143,7 @@ size_t backtrace_get(uintptr_t* frames, size_t frame_count) { return state.cur_frame; } -void backtrace_log(uintptr_t* frames, size_t frame_count) { +void backtrace_log(const uintptr_t* frames, size_t frame_count) { ScopedDisableDebugCalls disable; for (size_t frame_num = 0; frame_num < frame_count; frame_num++) { diff --git a/libc/malloc_debug/backtrace.h b/libc/malloc_debug/backtrace.h index 513a64950..9e010095f 100644 --- a/libc/malloc_debug/backtrace.h +++ b/libc/malloc_debug/backtrace.h @@ -35,6 +35,6 @@ void backtrace_startup(); void backtrace_shutdown(); size_t backtrace_get(uintptr_t* frames, size_t frame_count); -void backtrace_log(uintptr_t* frames, size_t frame_count); +void backtrace_log(const uintptr_t* frames, size_t frame_count); #endif // MALLOC_DEBUG_BACKTRACE_H diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp index f55d48822..4f8657969 100644 --- a/libc/malloc_debug/malloc_debug.cpp +++ b/libc/malloc_debug/malloc_debug.cpp @@ -88,7 +88,14 @@ static void LogTagError(const Header* header, const void* pointer, const char* n ScopedDisableDebugCalls disable; error_log(LOG_DIVIDER); - error_log("+++ ALLOCATION %p HAS INVALID TAG %" PRIx32 " (%s)", pointer, header->tag, name); + if (header->tag == DEBUG_FREE_TAG) { + error_log("+++ ALLOCATION %p USED AFTER FREE (%s)", pointer, name); + if (g_debug->config().options & FREE_TRACK) { + g_debug->free_track->LogBacktrace(header); + } + } else { + error_log("+++ ALLOCATION %p HAS INVALID TAG %" PRIx32 " (%s)", pointer, header->tag, name); + } error_log("Backtrace at time of failure:"); std::vector<uintptr_t> frames(64); size_t frame_num = backtrace_get(frames.data(), frames.size()); @@ -129,14 +136,12 @@ static void* InitHeader(Header* header, void* orig_pointer, size_t size) { if (g_debug->config().options & BACKTRACE) { BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header); if (g_debug->backtrace->enabled()) { - back_header->num_frames = backtrace_get(&back_header->frames[0], - g_debug->config().backtrace_frames); + back_header->num_frames = backtrace_get( + &back_header->frames[0], g_debug->config().backtrace_frames); backtrace_found = back_header->num_frames > 0; } else { back_header->num_frames = 0; } - back_header = g_debug->GetFreeBacktrace(header); - back_header->num_frames = 0; } if (g_debug->config().options & TRACK_ALLOCS) { @@ -313,18 +318,12 @@ void debug_free(void* pointer) { } if (g_debug->config().options & FREE_TRACK) { - // Only log the free backtrace if we are using the free track feature. - if ((g_debug->config().options & BACKTRACE) && g_debug->backtrace->enabled()) { - BacktraceHeader* back_header = g_debug->GetFreeBacktrace(header); - back_header->num_frames = backtrace_get(&back_header->frames[0], - g_debug->config().backtrace_frames); - } - g_debug->free_track->Add(*g_debug, header); // Do not free this pointer just yet. free_pointer = nullptr; } + header->tag = DEBUG_FREE_TAG; bytes = header->usable_size; } else { diff --git a/libc/malloc_debug/malloc_debug.h b/libc/malloc_debug/malloc_debug.h index 4a15f77bf..cd025a786 100644 --- a/libc/malloc_debug/malloc_debug.h +++ b/libc/malloc_debug/malloc_debug.h @@ -39,7 +39,6 @@ // will still be in this order. // Header (Required) // BacktraceHeader (Optional: For the allocation backtrace) -// BacktraceHeader (Optional: For the free backtrace) // uint8_t data (Optional: Front guard, will be a multiple of sizeof(uintptr_t)) // allocation data // uint8_t data (Optional: End guard) @@ -70,6 +69,7 @@ struct BacktraceHeader { } __attribute__((packed)); constexpr uint32_t DEBUG_TAG = 0x1ee7d00d; +constexpr uint32_t DEBUG_FREE_TAG = 0x1cc7dccd; constexpr char LOG_DIVIDER[] = "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***"; constexpr size_t FREE_TRACK_MEM_BUFFER_SIZE = 4096; diff --git a/libc/malloc_debug/tests/backtrace_fake.cpp b/libc/malloc_debug/tests/backtrace_fake.cpp index 32da696bb..db542e5e2 100644 --- a/libc/malloc_debug/tests/backtrace_fake.cpp +++ b/libc/malloc_debug/tests/backtrace_fake.cpp @@ -52,7 +52,7 @@ size_t backtrace_get(uintptr_t* frames, size_t frame_num) { return total_frames; } -void backtrace_log(uintptr_t* frames, size_t frame_count) { +void backtrace_log(const uintptr_t* frames, size_t frame_count) { for (size_t i = 0; i < frame_count; i++) { error_log(" #%02zd pc %p", i, reinterpret_cast<void*>(frames[i])); } diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp index 247e31979..551e49860 100644 --- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp +++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp @@ -99,9 +99,17 @@ std::string usage_string( "6 malloc_debug Instead, keep XX of these allocations around and then verify\n" "6 malloc_debug that they have not been modified when the total number of freed\n" "6 malloc_debug allocations exceeds the XX amount. When the program terminates,\n" - "6 malloc_debug the rest of these allocations are verified.\n" + "6 malloc_debug the rest of these allocations are verified. When this option is\n" + "6 malloc_debug enabled, it automatically records the backtrace at the time of the free.\n" "6 malloc_debug The default is to record 100 allocations.\n" "6 malloc_debug \n" + "6 malloc_debug free_track_backtrace_num_frames[=XX]\n" + "6 malloc_debug This option only has meaning if free_track is set. This indicates\n" + "6 malloc_debug how many backtrace frames to capture when an allocation is freed.\n" + "6 malloc_debug If XX is set, that is the number of frames to capture. If XX\n" + "6 malloc_debug is set to zero, then no backtrace will be captured.\n" + "6 malloc_debug The default is to record 16 frames.\n" + "6 malloc_debug \n" "6 malloc_debug leak_track\n" "6 malloc_debug Enable the leak tracking of memory allocations.\n" ); @@ -339,11 +347,13 @@ TEST_F(MallocDebugConfigTest, free_track) { ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options); ASSERT_EQ(1234U, config->free_track_allocations); ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes); + ASSERT_EQ(16U, config->free_track_backtrace_num_frames); ASSERT_TRUE(InitConfig("free_track")); ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options); ASSERT_EQ(100U, config->free_track_allocations); ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes); + ASSERT_EQ(16U, config->free_track_backtrace_num_frames); ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); @@ -354,11 +364,50 @@ TEST_F(MallocDebugConfigTest, free_track_and_fill_on_free) { ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options); ASSERT_EQ(1234U, config->free_track_allocations); ASSERT_EQ(32U, config->fill_on_free_bytes); + ASSERT_EQ(16U, config->free_track_backtrace_num_frames); ASSERT_TRUE(InitConfig("free_track fill_on_free=60")); ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options); ASSERT_EQ(100U, config->free_track_allocations); ASSERT_EQ(60U, config->fill_on_free_bytes); + ASSERT_EQ(16U, config->free_track_backtrace_num_frames); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames) { + ASSERT_TRUE(InitConfig("free_track_backtrace_num_frames=123")); + + ASSERT_EQ(0U, config->options); + ASSERT_EQ(123U, config->free_track_backtrace_num_frames); + + ASSERT_TRUE(InitConfig("free_track_backtrace_num_frames")); + ASSERT_EQ(0U, config->options); + ASSERT_EQ(16U, config->free_track_backtrace_num_frames); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames_zero) { + ASSERT_TRUE(InitConfig("free_track_backtrace_num_frames=0")); + + ASSERT_EQ(0U, config->options); + ASSERT_EQ(0U, config->free_track_backtrace_num_frames); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames_and_free_track) { + ASSERT_TRUE(InitConfig("free_track free_track_backtrace_num_frames=123")); + ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options); + ASSERT_EQ(123U, config->free_track_backtrace_num_frames); + + ASSERT_TRUE(InitConfig("free_track free_track_backtrace_num_frames")); + ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options); + ASSERT_EQ(16U, config->free_track_backtrace_num_frames); ASSERT_STREQ("", getFakeLogBuf().c_str()); ASSERT_STREQ("", getFakeLogPrint().c_str()); @@ -550,3 +599,13 @@ TEST_F(MallocDebugConfigTest, free_track_max_error) { "value must be <= 16384: 21000\n"); ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str()); } + +TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames_max_error) { + ASSERT_FALSE(InitConfig("free_track_backtrace_num_frames=400")); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string log_msg( + "6 malloc_debug malloc_testing: bad value for option 'free_track_backtrace_num_frames', " + "value must be <= 256: 400\n"); + ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str()); +} diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp index 08731c236..4b8aaeba1 100644 --- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp +++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp @@ -76,8 +76,7 @@ static size_t get_tag_offset(uint32_t flags = 0, size_t backtrace_frames = 0) { 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)); + offset += BIONIC_ALIGN(sizeof(BacktraceHeader) + sizeof(uintptr_t) * backtrace_frames, sizeof(uintptr_t)); } return offset; } @@ -209,7 +208,7 @@ TEST_F(MallocDebugTest, fill_on_alloc_partial) { } TEST_F(MallocDebugTest, fill_on_free) { - Init("fill_on_free free_track"); + Init("fill_on_free free_track free_track_backtrace_num_frames=0"); uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); ASSERT_TRUE(pointer != nullptr); @@ -226,7 +225,7 @@ TEST_F(MallocDebugTest, fill_on_free) { } TEST_F(MallocDebugTest, fill_on_free_partial) { - Init("fill_on_free=30 free_track"); + Init("fill_on_free=30 free_track free_track_backtrace_num_frames=0"); uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); ASSERT_TRUE(pointer != nullptr); @@ -246,7 +245,7 @@ TEST_F(MallocDebugTest, fill_on_free_partial) { } TEST_F(MallocDebugTest, free_track_partial) { - Init("fill_on_free=30 free_track"); + Init("fill_on_free=30 free_track free_track_backtrace_num_frames=0"); uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); ASSERT_TRUE(pointer != nullptr); @@ -688,7 +687,7 @@ TEST_F(MallocDebugTest, leak_track_frees) { } TEST_F(MallocDebugTest, free_track) { - Init("free_track=5"); + Init("free_track=5 free_track_backtrace_num_frames=0"); void* pointers[10]; for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) { @@ -714,7 +713,7 @@ TEST_F(MallocDebugTest, free_track) { } TEST_F(MallocDebugTest, free_track_use_after_free) { - Init("free_track=5"); + Init("free_track=5 free_track_backtrace_num_frames=0"); uint8_t* pointers[5]; for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) { @@ -779,7 +778,7 @@ TEST_F(MallocDebugTest, free_track_use_after_free) { } TEST_F(MallocDebugTest, free_track_use_after_free_finalize) { - Init("free_track=100"); + Init("free_track=100 free_track_backtrace_num_frames=0"); uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); ASSERT_TRUE(pointer != nullptr); @@ -803,10 +802,8 @@ TEST_F(MallocDebugTest, free_track_use_after_free_finalize) { } TEST_F(MallocDebugTest, free_track_use_after_free_with_backtrace) { - Init("free_track=100 backtrace"); + Init("free_track=100"); - // Alloc backtrace. - backtrace_fake_add(std::vector<uintptr_t> {0xf0, 0xe, 0xd}); // Free backtrace. backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc}); @@ -835,6 +832,72 @@ TEST_F(MallocDebugTest, free_track_use_after_free_with_backtrace) { ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); } +TEST_F(MallocDebugTest, free_track_use_after_free_call_realloc) { + Init("free_track=100"); + + // Free backtrace. + backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc}); + // Backtrace at realloc. + backtrace_fake_add(std::vector<uintptr_t> {0x12, 0x22, 0x32, 0x42}); + + void* pointer = debug_malloc(200); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 200); + debug_free(pointer); + + // Choose a size that should not trigger a realloc to verify tag is + // verified early. + ASSERT_TRUE(debug_realloc(pointer, 200) == nullptr); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ ALLOCATION %p USED AFTER FREE (realloc)\n", pointer); + expected_log += "6 malloc_debug Backtrace of original 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 += "6 malloc_debug Backtrace at time of failure:\n"; + expected_log += "6 malloc_debug #00 pc 0x12\n"; + expected_log += "6 malloc_debug #01 pc 0x22\n"; + expected_log += "6 malloc_debug #02 pc 0x32\n"; + expected_log += "6 malloc_debug #03 pc 0x42\n"; + expected_log += DIVIDER; + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugTest, free_track_use_after_free_call_free) { + Init("free_track=100"); + + // Free backtrace. + backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc}); + // Backtrace at second free. + backtrace_fake_add(std::vector<uintptr_t> {0x12, 0x22, 0x32, 0x42}); + + void* pointer = debug_malloc(200); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 200); + debug_free(pointer); + + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string expected_log(DIVIDER); + expected_log += android::base::StringPrintf( + "6 malloc_debug +++ ALLOCATION %p USED AFTER FREE (free)\n", pointer); + expected_log += "6 malloc_debug Backtrace of original 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 += "6 malloc_debug Backtrace at time of failure:\n"; + expected_log += "6 malloc_debug #00 pc 0x12\n"; + expected_log += "6 malloc_debug #01 pc 0x22\n"; + expected_log += "6 malloc_debug #02 pc 0x32\n"; + expected_log += "6 malloc_debug #03 pc 0x42\n"; + expected_log += DIVIDER; + ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); +} + TEST_F(MallocDebugTest, get_malloc_leak_info_invalid) { Init("fill"); |