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
diff --git a/libc/malloc_debug/BacktraceData.cpp b/libc/malloc_debug/BacktraceData.cpp
index 9f39068..61267f0 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 10daec7..842e372 100644
--- a/libc/malloc_debug/BacktraceData.h
+++ b/libc/malloc_debug/BacktraceData.h
@@ -44,14 +44,12 @@
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 224bb08..032c1fc 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -195,9 +195,17 @@
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 @@
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 @@
// 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 4b91e2b..d2cc56d 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -61,6 +61,7 @@
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 e023c51..40978db 100644
--- a/libc/malloc_debug/DebugData.h
+++ b/libc/malloc_debug/DebugData.h
@@ -67,11 +67,6 @@
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 3466861..3ac54bf 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 @@
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 @@
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 @@
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 @@
}
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 5888a0e..804b5a6 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 @@
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 @@
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 00290fd..8549dc4 100644
--- a/libc/malloc_debug/backtrace.cpp
+++ b/libc/malloc_debug/backtrace.cpp
@@ -143,7 +143,7 @@
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 513a649..9e01009 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 f55d488..4f86579 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -88,7 +88,14 @@
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 @@
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 @@
}
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 4a15f77..cd025a7 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 @@
} __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 32da696..db542e5 100644
--- a/libc/malloc_debug/tests/backtrace_fake.cpp
+++ b/libc/malloc_debug/tests/backtrace_fake.cpp
@@ -52,7 +52,7 @@
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 247e319..551e498 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 @@
"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 @@
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 @@
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 @@
"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 08731c2..4b8aaeb 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 @@
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_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_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, 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, 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_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_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_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 @@
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");