summaryrefslogtreecommitdiff
path: root/libc/malloc_debug/malloc_debug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libc/malloc_debug/malloc_debug.cpp')
-rw-r--r--libc/malloc_debug/malloc_debug.cpp113
1 files changed, 90 insertions, 23 deletions
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 53fcead01..609f030bf 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -30,11 +30,13 @@
#include <inttypes.h>
#include <malloc.h>
#include <pthread.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include <sys/param.h>
+#include <sys/syscall.h>
#include <unistd.h>
#include <mutex>
@@ -43,8 +45,10 @@
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
-#include <private/bionic_malloc_dispatch.h>
+#include <bionic/malloc_tagged_pointers.h>
+#include <platform/bionic/reserved_signals.h>
#include <private/MallocXmlElem.h>
+#include <private/bionic_malloc_dispatch.h>
#include "Config.h"
#include "DebugData.h"
@@ -91,8 +95,8 @@ struct mallinfo debug_mallinfo();
int debug_mallopt(int param, int value);
int debug_malloc_info(int options, FILE* fp);
int debug_posix_memalign(void** memptr, size_t alignment, size_t size);
-int debug_iterate(uintptr_t base, size_t size,
- void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+int debug_malloc_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
void debug_malloc_disable();
void debug_malloc_enable();
@@ -130,6 +134,40 @@ class ScopedConcurrentLock {
};
pthread_rwlock_t ScopedConcurrentLock::lock_;
+// Use this because the sigprocmask* functions filter out the reserved bionic
+// signals including the signal this code blocks.
+static inline int __rt_sigprocmask(int how, const sigset64_t* new_set, sigset64_t* old_set,
+ size_t sigset_size) {
+ return syscall(SYS_rt_sigprocmask, how, new_set, old_set, sigset_size);
+}
+
+// Need to block the backtrace signal while in malloc debug routines
+// otherwise there is a chance of a deadlock and timeout when unwinding.
+// This can occur if a thread is paused while owning a malloc debug
+// internal lock.
+class ScopedBacktraceSignalBlocker {
+ public:
+ ScopedBacktraceSignalBlocker() {
+ sigemptyset64(&backtrace_set_);
+ sigaddset64(&backtrace_set_, BIONIC_SIGNAL_BACKTRACE);
+ sigset64_t old_set;
+ __rt_sigprocmask(SIG_BLOCK, &backtrace_set_, &old_set, sizeof(backtrace_set_));
+ if (sigismember64(&old_set, BIONIC_SIGNAL_BACKTRACE)) {
+ unblock_ = false;
+ }
+ }
+
+ ~ScopedBacktraceSignalBlocker() {
+ if (unblock_) {
+ __rt_sigprocmask(SIG_UNBLOCK, &backtrace_set_, nullptr, sizeof(backtrace_set_));
+ }
+ }
+
+ private:
+ bool unblock_ = true;
+ sigset64_t backtrace_set_;
+};
+
static void InitAtfork() {
static pthread_once_t atfork_init = PTHREAD_ONCE_INIT;
pthread_once(&atfork_init, []() {
@@ -252,12 +290,19 @@ static void* InitHeader(Header* header, void* orig_pointer, size_t size) {
return g_debug->GetPointer(header);
}
+extern "C" void __asan_init() __attribute__((weak));
+
bool debug_initialize(const MallocDispatch* malloc_dispatch, bool* zygote_child,
const char* options) {
if (zygote_child == nullptr || options == nullptr) {
return false;
}
+ if (__asan_init != 0) {
+ error_log("malloc debug cannot be enabled alongside ASAN");
+ return false;
+ }
+
InitAtfork();
g_zygote_child = zygote_child;
@@ -326,8 +371,8 @@ void debug_finalize() {
void debug_get_malloc_leak_info(uint8_t** info, size_t* overall_size, size_t* info_size,
size_t* total_memory, size_t* backtrace_size) {
ScopedConcurrentLock lock;
-
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
// Verify the arguments.
if (info == nullptr || overall_size == nullptr || info_size == nullptr || total_memory == nullptr ||
@@ -362,6 +407,7 @@ size_t debug_malloc_usable_size(void* pointer) {
}
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
if (!VerifyPointer(pointer, "malloc_usable_size")) {
return 0;
@@ -426,6 +472,7 @@ void* debug_malloc(size_t size) {
}
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
void* pointer = InternalMalloc(size);
@@ -502,6 +549,7 @@ void debug_free(void* pointer) {
}
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
if (g_debug->config().options() & RECORD_ALLOCS) {
g_debug->record->AddEntry(new FreeEntry(pointer));
@@ -520,6 +568,7 @@ void* debug_memalign(size_t alignment, size_t bytes) {
}
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
if (bytes == 0) {
bytes = 1;
@@ -599,6 +648,7 @@ void* debug_realloc(void* pointer, size_t bytes) {
}
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
if (pointer == nullptr) {
pointer = InternalMalloc(bytes);
@@ -718,6 +768,7 @@ void* debug_calloc(size_t nmemb, size_t bytes) {
}
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
size_t size;
if (__builtin_mul_overflow(nmemb, bytes, &size)) {
@@ -778,16 +829,24 @@ int debug_malloc_info(int options, FILE* fp) {
if (DebugCallsDisabled() || !g_debug->TrackPointers()) {
return g_dispatch->malloc_info(options, fp);
}
+
+ // Make sure any pending output is written to the file.
+ fflush(fp);
+
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
- MallocXmlElem root(fp, "malloc", "version=\"debug-malloc-1\"");
+ // Avoid any issues where allocations are made that will be freed
+ // in the fclose.
+ int fd = fileno(fp);
+ MallocXmlElem root(fd, "malloc", "version=\"debug-malloc-1\"");
std::vector<ListInfoType> list;
PointerData::GetAllocList(&list);
size_t alloc_num = 0;
for (size_t i = 0; i < list.size(); i++) {
- MallocXmlElem alloc(fp, "allocation", "nr=\"%zu\"", alloc_num);
+ MallocXmlElem alloc(fd, "allocation", "nr=\"%zu\"", alloc_num);
size_t total = 1;
size_t size = list[i].size;
@@ -795,8 +854,8 @@ int debug_malloc_info(int options, FILE* fp) {
i++;
total++;
}
- MallocXmlElem(fp, "size").Contents("%zu", list[i].size);
- MallocXmlElem(fp, "total").Contents("%zu", total);
+ MallocXmlElem(fd, "size").Contents("%zu", list[i].size);
+ MallocXmlElem(fd, "total").Contents("%zu", total);
alloc_num++;
}
return 0;
@@ -827,7 +886,7 @@ int debug_posix_memalign(void** memptr, size_t alignment, size_t size) {
return (*memptr != nullptr) ? 0 : ENOMEM;
}
-int debug_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*),
+int debug_malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*),
void* arg) {
ScopedConcurrentLock lock;
if (g_debug->TrackPointers()) {
@@ -840,7 +899,7 @@ int debug_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_
// An option that adds a header will add pointer tracking, so no need to
// check if headers are enabled.
- return g_dispatch->iterate(base, size, callback, arg);
+ return g_dispatch->malloc_iterate(base, size, callback, arg);
}
void debug_malloc_disable() {
@@ -865,10 +924,12 @@ ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t max_fram
}
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
if (!(g_debug->config().options() & BACKTRACE)) {
return 0;
}
+ pointer = UntagPointer(pointer);
return PointerData::GetFrames(pointer, frames, max_frames);
}
@@ -898,27 +959,31 @@ void* debug_valloc(size_t size) {
static std::mutex g_dump_lock;
-static void write_dump(FILE* fp) {
- fprintf(fp, "Android Native Heap Dump v1.2\n\n");
+static void write_dump(int fd) {
+ dprintf(fd, "Android Native Heap Dump v1.2\n\n");
std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "unknown");
- fprintf(fp, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
+ dprintf(fd, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
- PointerData::DumpLiveToFile(fp);
+ PointerData::DumpLiveToFile(fd);
- fprintf(fp, "MAPS\n");
+ dprintf(fd, "MAPS\n");
std::string content;
if (!android::base::ReadFileToString("/proc/self/maps", &content)) {
- fprintf(fp, "Could not open /proc/self/maps\n");
+ dprintf(fd, "Could not open /proc/self/maps\n");
} else {
- fprintf(fp, "%s", content.c_str());
+ dprintf(fd, "%s", content.c_str());
}
- fprintf(fp, "END\n");
+ dprintf(fd, "END\n");
}
bool debug_write_malloc_leak_info(FILE* fp) {
+ // Make sure any pending output is written to the file.
+ fflush(fp);
+
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
std::lock_guard<std::mutex> guard(g_dump_lock);
@@ -926,23 +991,25 @@ bool debug_write_malloc_leak_info(FILE* fp) {
return false;
}
- write_dump(fp);
+ write_dump(fileno(fp));
+
return true;
}
void debug_dump_heap(const char* file_name) {
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
+ ScopedBacktraceSignalBlocker blocked;
std::lock_guard<std::mutex> guard(g_dump_lock);
- FILE* fp = fopen(file_name, "w+e");
- if (fp == nullptr) {
+ int fd = open(file_name, O_RDWR | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0644);
+ if (fd == -1) {
error_log("Unable to create file: %s", file_name);
return;
}
error_log("Dumping to file: %s\n", file_name);
- write_dump(fp);
- fclose(fp);
+ write_dump(fd);
+ close(fd);
}