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");