Merge "Add a CTS test to ensure AT_HWCAP2 is reported."
diff --git a/libc/malloc_debug/FreeTrackData.cpp b/libc/malloc_debug/FreeTrackData.cpp
index 3ac54bf..ed41981 100644
--- a/libc/malloc_debug/FreeTrackData.cpp
+++ b/libc/malloc_debug/FreeTrackData.cpp
@@ -67,18 +67,25 @@
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;
- while (bytes > 0) {
- size_t bytes_to_cmp = (bytes < cmp_mem_.size()) ? bytes : cmp_mem_.size();
- if (memcmp(memory, cmp_mem_.data(), bytes_to_cmp) != 0) {
- LogFreeError(debug, header, reinterpret_cast<const uint8_t*>(pointer));
- break;
+ if (header->tag != DEBUG_FREE_TAG) {
+ error_log(LOG_DIVIDER);
+ error_log("+++ ALLOCATION %p HAS CORRUPTED HEADER TAG 0x%x AFTER FREE", pointer, header->tag);
+ error_log(LOG_DIVIDER);
+ } else {
+ 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;
+ while (bytes > 0) {
+ size_t bytes_to_cmp = (bytes < cmp_mem_.size()) ? bytes : cmp_mem_.size();
+ if (memcmp(memory, cmp_mem_.data(), bytes_to_cmp) != 0) {
+ LogFreeError(debug, header, reinterpret_cast<const uint8_t*>(pointer));
+ break;
+ }
+ bytes -= bytes_to_cmp;
+ memory = &memory[bytes_to_cmp];
}
- 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));
@@ -98,7 +105,6 @@
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)));
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 568192d..1ee7689 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -112,6 +112,7 @@
);
});
}
+
static void LogTagError(const Header* header, const void* pointer, const char* name) {
ScopedDisableDebugCalls disable;
@@ -145,7 +146,7 @@
return nullptr;
}
header->usable_size -= g_debug->pointer_offset() +
- reinterpret_cast<uintptr_t>(orig_pointer) - reinterpret_cast<uintptr_t>(header);
+ reinterpret_cast<uintptr_t>(header) - reinterpret_cast<uintptr_t>(orig_pointer);
if (g_debug->config().options & FRONT_GUARD) {
uint8_t* guard = g_debug->GetFrontGuard(header);
@@ -326,8 +327,9 @@
void* free_pointer = pointer;
size_t bytes;
+ Header* header;
if (g_debug->need_header()) {
- Header* header = g_debug->GetHeader(pointer);
+ header = g_debug->GetHeader(pointer);
if (header->tag != DEBUG_TAG) {
LogTagError(header, pointer, "free");
return;
@@ -353,13 +355,6 @@
}
g_debug->track->Remove(header, backtrace_found);
}
-
- if (g_debug->config().options & FREE_TRACK) {
- 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;
@@ -373,7 +368,16 @@
memset(pointer, g_debug->config().fill_free_value, bytes);
}
- g_dispatch->free(free_pointer);
+ if (g_debug->config().options & FREE_TRACK) {
+ // Do not add the allocation until we are done modifying the pointer
+ // itself. This avoids a race if a lot of threads are all doing
+ // frees at the same time and we wind up trying to really free this
+ // pointer from another thread, while still trying to free it in
+ // this function.
+ g_debug->free_track->Add(*g_debug, header);
+ } else {
+ g_dispatch->free(free_pointer);
+ }
}
void* debug_memalign(size_t alignment, size_t bytes) {
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 2503981..b6bf7bc 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -24,6 +24,7 @@
#include <unistd.h>
#include <algorithm>
+#include <thread>
#include <vector>
#include <utility>
@@ -898,6 +899,53 @@
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
}
+TEST_F(MallocDebugTest, free_track_header_tag_corrupted) {
+ Init("free_track=100 free_track_backtrace_num_frames=0");
+
+ uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+ ASSERT_TRUE(pointer != nullptr);
+ memset(pointer, 0, 100);
+ debug_free(pointer);
+
+ pointer[-get_tag_offset()] = 0x00;
+
+ 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 HAS CORRUPTED HEADER TAG 0x1cc7dc00 AFTER FREE\n",
+ pointer);
+ expected_log += DIVIDER;
+ ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, free_track_multiple_thread) {
+ Init("free_track=10 free_track_backtrace_num_frames=0");
+
+ std::vector<std::thread*> threads(1000);
+ for (size_t i = 0; i < threads.size(); i++) {
+ threads[i] = new std::thread([](){
+ for (size_t j = 0; j < 100; j++) {
+ void* mem = debug_malloc(100);
+ write(0, mem, 0);
+ debug_free(mem);
+ }
+ });
+ }
+ for (size_t i = 0; i < threads.size(); i++) {
+ threads[i]->join();
+ delete threads[i];
+ }
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
TEST_F(MallocDebugTest, get_malloc_leak_info_invalid) {
Init("fill");