extern "C" void chk_free(void* ptr) { // log_message("%s: %s\n", __FILE__, __FUNCTION__); if (!ptr) /* ignore free(NULL) */ return; hdr_t* hdr = meta(ptr); if (del(hdr) < 0) { uintptr_t bt[MAX_BACKTRACE_DEPTH]; int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH); if (hdr->tag == BACKLOG_TAG) { log_message("+++ ALLOCATION %p SIZE %d BYTES MULTIPLY FREED!\n", user(hdr), hdr->size); log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->bt, hdr->bt_depth); /* hdr->freed_bt_depth should be nonzero here */ log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->freed_bt, hdr->freed_bt_depth); log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n", user(hdr), hdr->size); log_backtrace(bt, depth); } else { log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", user(hdr)); log_backtrace(bt, depth); } } else { hdr->freed_bt_depth = get_backtrace(hdr->freed_bt, MAX_BACKTRACE_DEPTH); add_to_backlog(hdr); } }
extern "C" void *chk_realloc(void *ptr, size_t size) { // log_message("%s: %s\n", __FILE__, __FUNCTION__); if (!ptr) { return chk_malloc(size); } #ifdef REALLOC_ZERO_BYTES_FREE if (!size) { chk_free(ptr); return NULL; } #endif hdr_t* hdr = meta(ptr); if (del(hdr) < 0) { uintptr_t bt[MAX_BACKTRACE_DEPTH]; int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH); if (hdr->tag == BACKLOG_TAG) { log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n", user(hdr), size, hdr->size); log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->bt, hdr->bt_depth); /* hdr->freed_bt_depth should be nonzero here */ log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->freed_bt, hdr->freed_bt_depth); log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n", user(hdr), hdr->size); log_backtrace(bt, depth); /* We take the memory out of the backlog and fall through so the * reallocation below succeeds. Since we didn't really free it, we * can default to this behavior. */ del_from_backlog(hdr); } else { log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", user(hdr), size); log_backtrace(bt, depth); // just get a whole new allocation and leak the old one return dlrealloc(0, size); // return dlrealloc(user(hdr), size); // assuming it was allocated externally } } hdr = static_cast<hdr_t*>(dlrealloc(hdr, sizeof(hdr_t) + size + sizeof(ftr_t))); if (hdr) { hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); add(hdr, size); return user(hdr); } return NULL; }
tor_assertion_failed_(const char *fname, unsigned int line, const char *func, const char *expr, const char *fmt, ...) { char *buf = NULL; char *extra = NULL; va_list ap; #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-nonliteral" #endif if (fmt) { va_start(ap,fmt); tor_vasprintf(&extra, fmt, ap); va_end(ap); } #ifdef __clang__ #pragma clang diagnostic pop #endif log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.", fname, line, func, expr); tor_asprintf(&buf, "Assertion %s failed in %s at %s:%u: %s", expr, func, fname, line, extra ? extra : ""); tor_free(extra); log_backtrace(LOG_ERR, LD_BUG, buf); tor_free(buf); }
static void ReportMemoryLeaks() { ScopedDisableDebugCalls disable; // Use /proc/self/exe link to obtain the program name for logging // purposes. If it's not available, we set it to "<unknown>". char exe[PATH_MAX]; int count; if ((count = readlink("/proc/self/exe", exe, sizeof(exe) - 1)) == -1) { strlcpy(exe, "<unknown>", sizeof(exe)); } else { exe[count] = '\0'; } if (g_allocated_block_count == 0) { log_message("+++ %s did not leak", exe); return; } size_t index = 1; const size_t total = g_allocated_block_count; while (head != NULL) { int safe; hdr_t* block = head; log_message("+++ %s leaked block of size %d at %p (leak %d of %d)", exe, block->size, user(block), index++, total); if (del_leak(block, &safe) && g_backtrace_enabled) { /* safe == 1, because the allocation is valid */ log_backtrace(block->bt, block->bt_depth); } } while (backlog_head != NULL) { del_from_backlog(backlog_tail); } }
/** * Logging interface with priority support * @param s A formated (printf-style) string to log */ void vLogError(const char *s, va_list ap) { va_list ap_copy; ASSERT(s); va_copy(ap_copy, ap); log_log(LOG_ERR, s, ap); va_end(ap_copy); log_backtrace(); }
static RETSIGTYPE sig_fatal (int sig) { sig_log_info(sig); #ifdef ENABLE_BACKTRACE hxd_log("Attempting backtrace..."); log_backtrace(); #endif _exit(sig); }
/** * Logging interface with priority support * @param s A formated (printf-style) string to log */ void LogError(const char *s, ...) { va_list ap; ASSERT(s); va_start(ap, s); log_log(LOG_ERR, s, ap); va_end(ap); log_backtrace(); }
/** * Logging interface with priority support * @param s A formated (printf-style) string to log */ void LogCritical(const char *s, ...) { va_list ap; ASSERT(s); va_start(ap, s); log_log(LOG_CRIT, s, ap); va_end(ap); log_backtrace(); }
/** * Logging interface with priority support * @param s A formated (printf-style) string to log */ void LogAlert(const char *s, ...) { va_list ap; ASSERT(s); va_start(ap, s); log_log(LOG_ALERT, s, ap); va_end(ap); log_backtrace(); }
/** * Logging interface with priority support * @param s A formated (printf-style) string to log */ void LogEmergency(const char *s, ...) { va_list ap; ASSERT(s); va_start(ap, s); log_log(LOG_EMERG, s, ap); va_end(ap); log_backtrace(); }
tor_bug_occurred_(const char *fname, unsigned int line, const char *func, const char *expr, int once, const char *fmt, ...) { char *buf = NULL; const char *once_str = once ? " (Future instances of this warning will be silenced.)": ""; if (! expr) { if (capturing_bugs()) { add_captured_bug("This line should not have been reached."); return; } log_warn(LD_BUG, "%s:%u: %s: This line should not have been reached.%s", fname, line, func, once_str); tor_asprintf(&buf, "Line unexpectedly reached at %s at %s:%u", func, fname, line); } else { if (capturing_bugs()) { add_captured_bug(expr); return; } va_list ap; char *extra = NULL; #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-nonliteral" #endif if (fmt) { va_start(ap,fmt); tor_vasprintf(&extra, fmt, ap); va_end(ap); } #ifdef __clang__ #pragma clang diagnostic pop #endif log_warn(LD_BUG, "%s:%u: %s: Non-fatal assertion %s failed.%s", fname, line, func, expr, once_str); tor_asprintf(&buf, "Non-fatal assertion %s failed in %s at %s:%u%s%s", expr, func, fname, line, fmt ? " : " : "", extra ? extra : ""); tor_free(extra); } log_backtrace(LOG_WARN, LD_BUG, buf); tor_free(buf); #ifdef TOR_UNIT_TESTS if (failed_assertion_cb) { failed_assertion_cb(); } #endif }
void fault_handler(int sig_nr) { VLOG_EMER("Caught signal %d.", sig_nr); log_backtrace(); fflush(stdout); fflush(stderr); signal(sig_nr, SIG_DFL); raise(sig_nr); }
/* returns 1 if valid, *safe == 1 if safe to dump stack */ static inline int check_allocation_locked(hdr_t* hdr, int* safe) { int valid = 1; *safe = 1; if (hdr->tag != ALLOCATION_TAG && hdr->tag != BACKLOG_TAG) { log_message("+++ ALLOCATION %p HAS INVALID TAG %08x (NOT DUMPING STACKTRACE)\n", user(hdr), hdr->tag); // Allocation header is probably corrupt, do not dequeue or dump stack // trace. *safe = 0; return 0; } if (hdr->tag == BACKLOG_TAG && was_used_after_free(hdr)) { log_message("+++ ALLOCATION %p SIZE %d WAS USED AFTER BEING FREED\n", user(hdr), hdr->size); valid = 0; /* check the guards to see if it's safe to dump a stack trace */ check_guards(hdr, safe); } else { valid = check_guards(hdr, safe); } if (!valid && *safe) { log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->bt, hdr->bt_depth); if (hdr->tag == BACKLOG_TAG) { log_message("+++ ALLOCATION %p SIZE %d FREED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->freed_bt, hdr->freed_bt_depth); } } return valid; }
extern "C" void* chk_realloc(void* ptr, size_t bytes) { // log_message("%s: %s\n", __FILE__, __FUNCTION__); if (DebugCallsDisabled()) { return g_malloc_dispatch->realloc(ptr, bytes); } if (!ptr) { return chk_malloc(bytes); } #ifdef REALLOC_ZERO_BYTES_FREE if (!bytes) { chk_free(ptr); return NULL; } #endif hdr_t* hdr = meta(ptr); if (del(hdr) < 0) { uintptr_t bt[MAX_BACKTRACE_DEPTH]; int depth = GET_BACKTRACE(bt, MAX_BACKTRACE_DEPTH); if (hdr->tag == BACKLOG_TAG) { log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n", user(hdr), bytes, hdr->size); if (g_backtrace_enabled) { log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->bt, hdr->bt_depth); /* hdr->freed_bt_depth should be nonzero here */ log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", user(hdr), hdr->size); log_backtrace(hdr->freed_bt, hdr->freed_bt_depth); log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n", user(hdr), hdr->size); log_backtrace(bt, depth); } /* We take the memory out of the backlog and fall through so the * reallocation below succeeds. Since we didn't really free it, we * can default to this behavior. */ del_from_backlog(hdr); } else { log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", user(hdr), bytes); if (g_backtrace_enabled) { log_backtrace(bt, depth); } // just get a whole new allocation and leak the old one return g_malloc_dispatch->realloc(0, bytes); // return realloc(user(hdr), bytes); // assuming it was allocated externally } } size_t size = sizeof(hdr_t) + bytes + sizeof(ftr_t); if (size < bytes) { // Overflow errno = ENOMEM; return NULL; } if (hdr->base != hdr) { // An allocation from memalign, so create another allocation and // copy the data out. void* newMem = g_malloc_dispatch->malloc(size); if (newMem == NULL) { return NULL; } memcpy(newMem, hdr, sizeof(hdr_t) + hdr->size); g_malloc_dispatch->free(hdr->base); hdr = static_cast<hdr_t*>(newMem); } else { hdr = static_cast<hdr_t*>(g_malloc_dispatch->realloc(hdr, size)); } if (hdr) { hdr->base = hdr; hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH); add(hdr, bytes); return user(hdr); } return NULL; }
static int traverseTree(MutexInfo* obj, MutexInfo const* objParent) { /* * Have we been here before? */ if (obj->historyMark) { int stackDepth; uintptr_t addrs[STACK_TRACE_DEPTH]; /* Turn off prediction temporarily in this thread while logging */ sPthreadDebugDisabledThread = gettid(); backtrace_startup(); LOGW("%s\n", kStartBanner); LOGW("pid: %d, tid: %d >>> %s <<<", getpid(), gettid(), __progname); LOGW("Illegal lock attempt:\n"); LOGW("--- pthread_mutex_t at %p\n", obj->mutex); stackDepth = get_backtrace(addrs, STACK_TRACE_DEPTH); log_backtrace(addrs, stackDepth); LOGW("+++ Currently held locks in this thread (in reverse order):"); MutexInfo* cur = obj; pid_t ourtid = gettid(); int i; for (i=0 ; i<cur->parents.count ; i++) { MutexInfo* parent = cur->parents.list[i]; if (parent->owner == ourtid) { LOGW("--- pthread_mutex_t at %p\n", parent->mutex); if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) { log_backtrace(parent->stackTrace, parent->stackDepth); } cur = parent; break; } } LOGW("+++ Earlier, the following lock order (from last to first) was established\n"); return 0; } obj->historyMark = 1; MutexInfoList* pList = &obj->children; int result = 1; int i; for (i = pList->count-1; i >= 0; i--) { MutexInfo* child = pList->list[i]; if (!traverseTree(child, obj)) { LOGW("--- pthread_mutex_t at %p\n", obj->mutex); if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) { int index = historyListHas(&obj->parents, objParent); if ((size_t)index < (size_t)obj->stacks.count) { log_backtrace(obj->stacks.stack[index].addrs, obj->stacks.stack[index].depth); } else { log_backtrace(obj->stackTrace, obj->stackDepth); } } result = 0; break; } } obj->historyMark = 0; return result; }
void sp_error() { LOGERROR("Unrecoverable error occured. Backtrace"); log_backtrace(); exit(1); }