内存检查Android malloc debug 源码解析

1,287 阅读16分钟

Malloc Debug

Malloc debug的源码在platform_bioniclibc\malloc_debug目录下。

选项

  • front_guard[=SIZE_BYTES]是在将要分配的内存之前增加一个信息段,来判断分配的内存是否存在越界行为。
  • rear_guard[=SIZE_BYTES]是在将要分配的内存之后增加一个信息段,来判断分配的内存是否存在越界行为。
  • guard[=SIZE_BYTES]front_guard[=SIZE_BYTES]rear_guard[=SIZE_BYTES]功能的并集。
  • free_track[=ALLOCATION_COUNT]: 通过一个list来记录free事件;
  • free_track_backtrace_num_frames[=MAX_FRAMES]: 记录free事件的回溯堆栈信息的最大深度;
  • leak_track: 跟踪所有的allocation事件,当程序终止时,所有的allocation事件都将被dump到log中,如果backtrace选项也开启,那么dump到log中的信息也包括泄露的allocation的调用栈信息;
  • record_allocs[=TOTAL_ENTRIES]: 记录所有线程的allocation/free事件,直到收到了SIGRTMAX - 18 (在安卓上是46)的信号量;
  • record_allocs_file[=FILE_NAME]: 设置record_allocs保存地址;
  • verify_pointers: 通过记录所有的"活着"的allocation, 进而决定一个指针是否有效;
  • backtrace[=MAX_FRAMES]: 记录每一个内存分配事件的backtrace信息;
  • more options // TODO

检查规则

堆内存越界(heap-buffer-overflow)

平台默认值的一些逻辑源码地址:libc\malloc_debug\DebugData.cpp (DebugData::Initialize)

主要逻辑源码地址:libc\malloc_debug\malloc_debug.cpp (debug_malloc, debug_free, debug_aligned_alloc, debug_memalign, debug_realloc, debug_calloc, debug_posix_memalign, debug_pvalloc, debug_valloc等)

通俗来说,所谓的边界检查其实就是在原本的malloc内存的基础上再扩大一部分内存,扩大的这部分内存用来记录原本的malloc内存是否有效

struct Header {
  uint32_t tag;              // malloc事件设置为 DEBUG_TAG, free事件设置为 DEBUG_FREE_TAG
  void* orig_pointer;        // 实际从堆中拿到的内存地址
  size_t size;               // hook到的要分配内存size的大小
  size_t usable_size;        // 实际分配的可用内存size的大小
} __attribute__((packed));
    // 0. Alignment 内存对齐的偏移
    // 1. Header的长度在64位机器上为32字节,在32位机器上为16字节
    // 2. front_guard和rear_guard大小在64位机器上为默认为32字节,在32位机器上默认为16字节
    // 3. 设置的guard区域大小,再64位机器上需要与16对齐,在32位机器上需要与8对齐
    // 4. front_guard用memset填满32或16字节大小的 0xaa 作为标记
    // 5. rear_guard用memset填满32或16字节大小的 0xbb 作为标记

   |     Header     |    front_guard       |   真正存储数据内存   |   rear_guard      |  Alignment   |
    ------------------------------------------------------------------------------------------------
   |                |                      |                     |                   |              |
    ------------------------------------------------------------------------------------------------

算法描述:

建立边界:

  1. hook到malloc等事件的发生;
  2. 根据64位和32位的不同,来计算Alignment, Header真正存储数据内存大小以及front_guardrear_guard大小,实际分配计算好的综合大小的内存;
  3. 找到Header所在的内存的地址,计算并写入tag, orig_pointer, size以及usable_size的数据;
  4. 找到真正存储数据内存的地址,返回给调用者。

查看是否存在越界行为:

  1. hook到free事件的发生;
  2. 根据free传入的指针,来通过偏移拿到Header的信息;
  3. 通过Header和偏移来拿到front_guardrear_guard的地址;
  4. 读取这两块地址的数据,通过与默认值对比来检查是否存在有越界行为;

细节描述:

1. Header结构体的作用

内存分配事件有malloc类,memalign类,对于malloc类事件而言,实际分配的内存大小就是size;而对于memalign类事件而言,实际分配的内存大小是根据alignmentbytes计算出来的。因此,要记录实际分配的内存大小和调用时输入的内存大小,就需要一个“recorder”来记录这些数据,而此处这个“recorder”就是Header

2. Header结构体的初始化,以及前置和后置边界检查内存的初始化逻辑

此处举两个例子,第一个是malloc事件:

void *malloc(size_t size ); 

当hook到malloc时:

  • 首先计算Header大小 + front_guard大小 + 参数bytes + rear_guard大小,得到一个要实际申请的内存大小;
  • 使用memalign按照32位或者64位上的对齐大小得到一个整块对齐之后的内存;
  • 由于malloc返回的地址不用对齐,因此内存布局如下:
    |  Header  | front_guard |     存储数据内存  |        rear_guard    |    alignment   |
    ------------------------------------------------------------------------------------
    |          |             |                  |                      |                |
    ------------------------------------------------------------------------------------
    // 此处的alignment 的值可能为0
  • 由于要返回给调用者的实际地址存储数据内存不需要对齐,因此Header的初始化就变得十分简单: tagDEBUG_TAG; orig_pointer就是Header部分的起始地址;size部分是malloc参数size; usable_size就是存储数据内存的大小;
void *memalign(size_t alignment , size_t bytes ); 

当hook到memalign时:

  • 首先计算Header大小 + front_guard大小 + 参数bytes + rear_guard大小,并按照32位或者64位上的对齐大小得到一个对齐之后的要实际申请的内存大小;
  • 随后为了避免不同平台内部allocatior的对齐规则,因此直接使用了malloc来申请要实际申请的内存大小的内存(We don't have any idea what the natural alignment of the underlying native allocator is, so we always need to over allocate.);
  • 需要注意的是,此时malloc之后分配的内存,是包含了四个部分的内存总和,但实际需要返回调用者的只是参数bytes大小的内存,而该块内存必须是对齐的,因此我们必须进行计算。
    // 假设原本的内存布局如下:
    
    |  Header  | front_guard |    存储数据内存  |        rear_guard    |              alignment              |
    -------------------------------------------------------------------------------------------------------
    |          |             |                 |                      |                                     |
    -------------------------------------------------------------------------------------------------------

    // 但存储数据内存区域的起始地址没有对齐,因此我们必须经过计算, 最中内存布局如下:
    
    |  alignment_1   |  Header  | front_guard |     存储数据内存   |      rear_guard      |    alignment_2  |
    -------------------------------------------------------------------------------------------------------
    |                |          |             |                   |                      |                 |
    // 对齐之后,可能存在着alignment_2,也可能不存在着alignment_2,但无论怎么样, alignment = alignment_1 + alignment_2
  • 完成对齐之后,Header的起始地址就等于存储数据内存 - front_guard大小 + Header大小,而Header中的tagDEBUG_TAG; orig_pointer就是alignment_1部分的起始地址;size部分是bytes; usable_size就是存储数据内存的大小

检验案例:heap-buffer-overflow

分配内存过大(allocation-size-too-big)

源码地址:libc\malloc_debug\malloc_debug.cpp (InternalMalloc)

默认最大可分配的内存大小为(1U << 31) - 1,只要要分配内存大小大于这个值,就视为分配内存过大,随即置errnoENOMEM, 返回nullptr

void *malloc(size_t size ); 
  static size_t MaxSize() { return (1U << 31) - 1; }

  if (size > PointerInfoType::MaxSize()) {
    errno = ENOMEM;
    return nullptr;
  }

校验案例:allocation-size-too-big

calloc溢出(calloc-overflow)

void *calloc(size_t num, size_t size);

calloc调用之前计算其numsize的乘积,也就是即将要分配内存的大小,若该大小通过__builtin_mul_overflow__builtin_add_overflow函数被判定为overflow,则就是calloc溢出。

  if (__builtin_mul_overflow(num, bytes, &size)) {
    // Overflow
    errno = ENOMEM;
    return nullptr;
  }
  // 此处的g_debug->extra_bytes()大小为 = Header大小 + 前置边界检查内存大小 + 后置边界检查内存大小
  if (__builtin_add_overflow(size, g_debug->extra_bytes(), &real_size)) {
    // Overflow.
    errno = ENOMEM;
    return nullptr;
  }

校验案例:calloc-overflow

free后再次使用堆内存(heap-use-after-free)

源码地址:libc\malloc_debug\PointerData.cpp (PointerData::VerifyFreedPointer, PointerData::AddFreed, )

  1. 存在一个用于记录free事件的隔离区free_pointers_,该隔离区默认可记录free事件的数量是100;
  2. 若一个新的free事件到达,则将其内存的实际存储数据部分写满0xef
  3. 当malloc debug结束运行时,遍历整个隔离区,判断隔离区内的所有free事件的实际存储数据部分是否全为0xef,不是则说明该段内存在free之后还被使用;

校验案例: heap-use-after-free

二次释放(double-free)

源码地址:libc\malloc_debug\PointerData.cpp (PointerData::VerifyAllFreed, PointerData::AddFreed, PointerData::LogFreeBacktrace)

  1. 存在一个用于记录free事件的隔离区free_pointers_,该隔离区默认可记录free事件的数量是100;
  2. 若新到达的free事件已经存在于隔离区内,则判定为二次释放;
  3. 若新到达的free事件没有在隔离区内,则执行下列操作:
    • 若新到达的free事件没有引起隔离区超出大小限制,将要free的内存添加到该隔离区内,并且不调用free来释放内存;
    • 若新到达的free事件导致隔离区超出了大小限制,则将隔离区内“最古老”的free事件清除,调用free来释放该段内存, 之后将新到达的free事件的内存添加到该隔离区内。

校验案例:double-free

无效的分配对齐(invalid-allocation-alignment)

源码地址:libc\malloc_debug\malloc_debug.cpp (debug_aligned_alloc)

void *aligned_alloc(size_t alignment , size_t size ); 

调用aligned_alloc之前,通过检查alignment是否是2的倍数,以及size % alignment是否为0来判定是否存在无效的分配对齐,若无效,将errno置为EINVAL, 返回nullptr

  if (!powerof2(alignment) || (size % alignment) != 0) {
    errno = EINVAL;
    return nullptr;
  }

校验案例:invalid-allocation-alignment

内存泄漏(leak)

存在一个用来记录所有内存分配、销毁地址,以及该指针的一些信息的表。例如,当一个malloc事件发生,malloc获得的内存就会被记录在该表中,若该内存使用了free销毁,则该内存就会被remove出这个表。因此在程序结束阶段,该表中只会保存着没有被free的内存,也就是内存泄漏的内存。

校验案例:leak

Map

init

  • debug_initialize
    • debug->Initialize(options): 一些规则检查选项开关的初始化,以及一些偏移(例如guard大小)的计算等;
    • Unreachable::Initialize(debug->config()): 只有开启了check_unreachable_on_signal选项才会屏蔽一些信号量
    • 初始化全局的单例DebugData* g_debug;
    • backtrace_startup, 默认一直开启回溯功能;
      • _Unwind_Backtrace(find_current_map, nullptr);
    • ScopedConcurrentLock::Init();:读写锁的初始化

debug_malloc

  • void* debug_malloc(size_t size);
    • 加锁;
    • void* pointer = InternalMalloc(size);
      • (g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset(): 若开启了BACKTRACE选项以及接收到了信号导致的backtrace_dump_标识为true,则dump目前所有记录的信息到默认地址文件/data/local/tmp/backtrace_heap/<pid>.txt中;
    • g_debug->record->AddEntry(new MallocEntry(pointer, size));: 如果开启了RECORD_ALLOCS选项,则将记录到的一切数据写入该文件中;
    • real_size = size + g_debug->extra_bytes();: 计算要分配的实际内存;
    • 若开启边界检查,则会先调用memalign申请对齐过后realsize大小内存,再对该段内存初始化; 若没有开启边界检查,则会直接调用malloc申请realsize大小内存;
    • pointer = InitHeader(header, header, size);
      • 初始化Header,guard的数据, 并返回实际供用于使用的内存地址;
      • 若开启了TRACK_ALLOCS选项,就将申请的pointer以及size信息保存到pointers_哈希表中;
      • 若开启了FILL_ON_ALLOC选项,就将pointer大小的内存全部填充为0xeb;
    • 若开启了RECORD_ALLOCS, 将申请的pointer以及size信息保存到用于记录分配内存的表entries_中;
void* debug_malloc(size_t size) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled()) {
    return g_dispatch->malloc(size);
  }
  ScopedConcurrentLock lock;
  ScopedDisableDebugCalls disable;
  ScopedBacktraceSignalBlocker blocked;

  void* pointer = InternalMalloc(size);   //  此处的pointer就是要返回给调用者的地址

  if (g_debug->config().options() & RECORD_ALLOCS) {   //  将所有的alloc事件记录
    g_debug->record->AddEntry(new MallocEntry(pointer, size));
  }

  return pointer;
}
static void* InternalMalloc(size_t size) {
  if ((g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
    debug_dump_heap(android::base::StringPrintf(     // 符合条件就将数据写入到文件中
                        "%s.%d.txt", g_debug->config().backtrace_dump_prefix().c_str(), getpid())
                        .c_str());
  }

  if (size == 0) {
    size = 1;
  }

  size_t real_size = size + g_debug->extra_bytes();    //  要分配的内存加上`Header`大小, `front_guard`大小
  if (real_size < size) {
    // Overflow.
    errno = ENOMEM;
    return nullptr;
  }

  if (size > PointerInfoType::MaxSize()) {
    errno = ENOMEM;
    return nullptr;
  }

  void* pointer;
  if (g_debug->HeaderEnabled()) {
    // 此处使用memalign的用意在于,获得一块对齐的内存
    Header* header =
        reinterpret_cast<Header*>(g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size));
    if (header == nullptr) {
      return nullptr;
    }
    // 初始化 Header,其实也就是初始化申请的原始内存块
    // 注意: 这里的两个参数都是header, 具体原因看 堆内存越界(`heap-buffer-overflow`) 一节
    pointer = InitHeader(header, header, size);   
  } else {
    pointer = g_dispatch->malloc(real_size);  // 没有开启边界检查,则调用原始的 malloc
  }

  if (pointer != nullptr) {
    if (g_debug->TrackPointers()) {   // 开启TRACK_ALLOCS选项,
      PointerData::Add(pointer, size);   // 将返回给调用者的内存记录到 pointers_ 哈希表中
    }

    if (g_debug->config().options() & FILL_ON_ALLOC) {   // 开启 FILL_ON_ALLOC 选项, 初始化内存数据
      size_t bytes = InternalMallocUsableSize(pointer);
      size_t fill_bytes = g_debug->config().fill_on_alloc_bytes();
      bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
      memset(pointer, g_debug->config().fill_alloc_value(), bytes);
    }
  }
  return pointer;
}

static void* InitHeader(Header* header, void* orig_pointer, size_t size) {
  // 设置 header 数据
  header->tag = DEBUG_TAG;
  header->orig_pointer = orig_pointer;
  header->size = size;
  header->usable_size = g_dispatch->malloc_usable_size(orig_pointer);   // 此处拿到的是分配函数返回的pointer所申请的内存实际大小;
  if (header->usable_size == 0) {
    g_dispatch->free(orig_pointer);
    return nullptr;
  }
  // 注意,这一步为什么要这么计算usable_size的原因,在 堆内存越界(`heap-buffer-overflow`) 一节 中有体现, 
  header->usable_size -= g_debug->pointer_offset() + reinterpret_cast<uintptr_t>(header) -
                         reinterpret_cast<uintptr_t>(orig_pointer);

  if (g_debug->config().options() & FRONT_GUARD) {  // 开启 FRONT_GUARD
    // 拿到front_guard区域的地址
    uint8_t* guard = g_debug->GetFrontGuard(header);
    // rear_guard 区域内的数据全部写为 0xaa
    memset(guard, g_debug->config().front_guard_value(), g_debug->config().front_guard_bytes());
  }

  if (g_debug->config().options() & REAR_GUARD) { // 开启 REAR_GUARD
    // 拿到rear_guard区域的地址
    uint8_t* guard = g_debug->GetRearGuard(header);
    // rear_guard 区域内的数据全部写为 0xbb
    memset(guard, g_debug->config().rear_guard_value(), g_debug->config().rear_guard_bytes());
    // If the rear guard is enabled, set the usable size to the exact size
    // of the allocation.
    header->usable_size = header->size;   // 如果两个guard都开启,那么真正可用的数据就是申请到的内存
  }

  return g_debug->GetPointer(header);
}

debug_calloc

  • void* debug_calloc(size_t nmemb, size_t bytes);
    • 加锁;
    • 通过__builtin_mul_overflow以及__builtin_add_overflow判断要申请的内存大小是否溢出;
    • real_size = size + g_debug->extra_bytes();: 计算要分配的实际内存;
    • 判断realsize是否太大;
    • 若开启边界检查,首先调用g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size)分配内存;
    • memset(header, 0, g_dispatch->malloc_usable_size(header));: 使用memset将分配到的内存置0;
    • pointer = InitHeader(header, header, size);: 调用InitHeader初始化;
      • InitHeader
    • 若开启了RECORD_ALLOCS, 将申请的pointer以及size信息保存到用于记录分配内存的表entries_中;
    • 若开启了TRACK_ALLOCS选项,就将申请的pointer以及size信息保存到pointers_哈希表中;
void* debug_calloc(size_t nmemb, size_t bytes) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled()) {
    return g_dispatch->calloc(nmemb, bytes);
  }
  ScopedConcurrentLock lock;
  ScopedDisableDebugCalls disable;
  ScopedBacktraceSignalBlocker blocked;

  size_t size;
  if (__builtin_mul_overflow(nmemb, bytes, &size)) {   // 是否溢出
    // Overflow
    errno = ENOMEM;
    return nullptr;
  }

  if (size == 0) {
    size = 1;
  }

  size_t real_size;
  if (__builtin_add_overflow(size, g_debug->extra_bytes(), &real_size)) {  // 是否溢出
    // Overflow.
    errno = ENOMEM;
    return nullptr;
  }

  if (real_size > PointerInfoType::MaxSize()) {
    errno = ENOMEM;
    return nullptr;
  }

  void* pointer;
  if (g_debug->HeaderEnabled()) {
    // Need to guarantee the alignment of the header.
    Header* header =
        reinterpret_cast<Header*>(g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size));
    if (header == nullptr) {
      return nullptr;
    }
    memset(header, 0, g_dispatch->malloc_usable_size(header));  // calloc自带初始化功能,这里也是同样的逻辑
    pointer = InitHeader(header, header, size);
  } else {
    pointer = g_dispatch->calloc(1, real_size);
  }

  if (g_debug->config().options() & RECORD_ALLOCS) {
    g_debug->record->AddEntry(new CallocEntry(pointer, bytes, nmemb));
  }

  if (pointer != nullptr && g_debug->TrackPointers()) {
    PointerData::Add(pointer, size);
  }
  return pointer;
}

debug_realloc

  • void* debug_realloc(void* pointer, size_t bytes);
    • 加锁;
    • 如果pointer是空指针,则直接调用InternalMalloc分配内存并返回;
    • if (!VerifyPointer(pointer, "realloc")): pointer不是空指针后则验证该段内存的合法性;
    • if (bytes == 0): bytes为0,则调用InternalFree对该段内存free, 并返回nullptr;
    • 若bytes和pointer都有效,则最终会将原来的内存地址进行InternalFree, 并计算新要分配的内存大小,对其进行InternalMalloc;
    • 最终返回新的内存地址;
void* debug_realloc(void* pointer, size_t bytes) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled()) {
    return g_dispatch->realloc(pointer, bytes);
  }
  ScopedConcurrentLock lock;
  ScopedDisableDebugCalls disable;
  ScopedBacktraceSignalBlocker blocked;

  if (pointer == nullptr) {   // 原始为空指针,直接分配就好
    pointer = InternalMalloc(bytes);
    if (g_debug->config().options() & RECORD_ALLOCS) {
      g_debug->record->AddEntry(new ReallocEntry(pointer, bytes, nullptr));
    }
    return pointer;
  }

  if (!VerifyPointer(pointer, "realloc")) {   
    return nullptr;
  }

  if (bytes == 0) {   // 重新分配的大小为0,free掉原来内存返回nullptr
    if (g_debug->config().options() & RECORD_ALLOCS) {
      g_debug->record->AddEntry(new ReallocEntry(nullptr, bytes, pointer));
    }

    InternalFree(pointer);
    return nullptr;
  }

  size_t real_size = bytes;
  if (g_debug->config().options() & EXPAND_ALLOC) {  // 是否开启 EXPAND_ALLOC
    real_size += g_debug->config().expand_alloc_bytes();
    if (real_size < bytes) {
      // Overflow.
      errno = ENOMEM;
      return nullptr;
    }
  }

  if (bytes > PointerInfoType::MaxSize()) {   // 是否溢出
    errno = ENOMEM;
    return nullptr;
  }

  void* new_pointer;
  size_t prev_size;
  if (g_debug->HeaderEnabled()) {
    // Same size, do nothing.
    Header* header = g_debug->GetHeader(pointer);
    if (real_size == header->size) {    // 要申请的内存大小和原来大小一样,什么都不做,只是将该段内存的信息更新一下
      if (g_debug->TrackPointers()) {
        // Remove and re-add so that the backtrace is updated.
        PointerData::Remove(pointer);
        PointerData::Add(pointer, real_size);
      }
      return pointer;
    }

    // Allocation is shrinking.
    // 要申请的内存比原来内存小, 不free, 只是多余的内存填充为 0xbb,用作 rear_guard
    if (real_size < header->usable_size) {   
      header->size = real_size;
      if (g_debug->config().options() & REAR_GUARD) {
        // Don't bother allocating a smaller pointer in this case, simply
        // change the header usable_size and reset the rear guard.
        header->usable_size = header->size;
        memset(g_debug->GetRearGuard(header), g_debug->config().rear_guard_value(),
               g_debug->config().rear_guard_bytes());
      }
      if (g_debug->TrackPointers()) {   // 更新这块内存信息
        // Remove and re-add so that the backtrace is updated.
        PointerData::Remove(pointer);
        PointerData::Add(pointer, real_size);
      }
      return pointer;
    }

    // Allocate the new size.
    // 要申请的内存比原来内存大
    new_pointer = InternalMalloc(bytes);   // 先申请内存
    if (new_pointer == nullptr) {
      errno = ENOMEM;
      return nullptr;
    }

    prev_size = header->usable_size;
    memcpy(new_pointer, pointer, prev_size);  // 将原来内存块上的数据拷贝到新的内存块
    InternalFree(pointer);   // free掉原来的内存块
  } else {
    // 没有开启边界检查
    if (g_debug->TrackPointers()) {
      PointerData::Remove(pointer);   // 从 pointers_ 哈希表中删去pointer的信息
    }

    prev_size = g_dispatch->malloc_usable_size(pointer);
    new_pointer = g_dispatch->realloc(pointer, real_size);   // 分配新的内存块
    if (new_pointer == nullptr) {
      return nullptr;
    }

    if (g_debug->TrackPointers()) {
      PointerData::Add(new_pointer, real_size);   // 将新的内存卡的信息记录
    }
  }

  if (g_debug->config().options() & FILL_ON_ALLOC) { // 是否开启 FILL_ON_ALLOC
    size_t bytes = InternalMallocUsableSize(new_pointer);
    if (bytes > g_debug->config().fill_on_alloc_bytes()) {
      bytes = g_debug->config().fill_on_alloc_bytes();
    }
    if (bytes > prev_size) {
      memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(new_pointer) + prev_size),
             g_debug->config().fill_alloc_value(), bytes - prev_size);
    }
  }

  if (g_debug->config().options() & RECORD_ALLOCS) {  // 是否开启 RECORD_ALLOCS
    g_debug->record->AddEntry(new ReallocEntry(new_pointer, bytes, pointer));
  }

  return new_pointer;
}

debug_memalign

  • void* debug_memalign(size_t alignment, size_t bytes);
    • 加锁;
    • 判断bytes是否有效;
    • 若开启边界检查,则要进行对齐运算, 最终得到一个对齐的realsize;
    • pointer = g_dispatch->malloc(real_size);: 使用malloc分配一个realsize大小的内存;
    • value += (-value % alignment);: 因为要返回的地址一定得是对齐的,因此需要调整申请到的内存内部的布局;
    • pointer = InitHeader(header, pointer, bytes);
    • 一些选项的处理
void* debug_memalign(size_t alignment, size_t bytes) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled()) {
    return g_dispatch->memalign(alignment, bytes);
  }
  ScopedConcurrentLock lock;
  ScopedDisableDebugCalls disable;
  ScopedBacktraceSignalBlocker blocked;

  if (bytes == 0) {
    bytes = 1;
  }

  if (bytes > PointerInfoType::MaxSize()) {   // 是否溢出
    errno = ENOMEM;
    return nullptr;
  }

  void* pointer;
  if (g_debug->HeaderEnabled()) {
    // 开启边界检查
    // Make the alignment a power of two.
    if (!powerof2(alignment)) {   // alignment是否与2对齐
      alignment = BIONIC_ROUND_UP_POWER_OF_2(alignment);  // 对齐alignment
    }
    // Force the alignment to at least MINIMUM_ALIGNMENT_BYTES to guarantee
    // that the header is aligned properly.
    if (alignment < MINIMUM_ALIGNMENT_BYTES) {  // 至少以8,16当作在32,64位机器上的最小对齐准则
      alignment = MINIMUM_ALIGNMENT_BYTES;
    }

    // We don't have any idea what the natural alignment of
    // the underlying native allocator is, so we always need to
    // over allocate.
    size_t real_size = alignment + bytes + g_debug->extra_bytes();  // alignment + 要分配的大小 + Header大小 + guard大小
    if (real_size < bytes) {
      // Overflow.
      errno = ENOMEM;
      return nullptr;
    }

    pointer = g_dispatch->malloc(real_size);   //  此处调用malloc的原因在堆内存越界(`heap-buffer-overflow`) 一节 中有体现, 
    if (pointer == nullptr) {
      return nullptr;
    }

    // 这里拿到的是要给调用者返回的地址, 该地址在memalgin的函数中必须是要对齐的
    uintptr_t value = reinterpret_cast<uintptr_t>(pointer) + g_debug->pointer_offset();  
    // Now align the pointer.
    value += (-value % alignment);   // 计算对齐该地址需要调整的字节

    Header* header = g_debug->GetHeader(reinterpret_cast<void*>(value));   //  
    // 因为此时有调整的对齐字节,因此header, pointer的地址也不一致, 原因在堆内存越界(`heap-buffer-overflow`) 一节 中有体现, 
    pointer = InitHeader(header, pointer, bytes);
  } else {
    // 没有开启边界检查
    size_t real_size = bytes + g_debug->extra_bytes();
    if (real_size < bytes) {
      // Overflow.
      errno = ENOMEM;
      return nullptr;
    }
    pointer = g_dispatch->memalign(alignment, real_size);  // 直接调用原生的memalign
  }

  if (pointer != nullptr) {
    if (g_debug->TrackPointers()) {
      PointerData::Add(pointer, bytes);
    }

    if (g_debug->config().options() & FILL_ON_ALLOC) {
      size_t bytes = InternalMallocUsableSize(pointer);
      size_t fill_bytes = g_debug->config().fill_on_alloc_bytes();
      bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
      memset(pointer, g_debug->config().fill_alloc_value(), bytes);
    }

    if (g_debug->config().options() & RECORD_ALLOCS) {
      g_debug->record->AddEntry(new MemalignEntry(pointer, bytes, alignment));
    }
  }

  return pointer;
}

debug_aligned_alloc

  • void* debug_aligned_alloc(size_t alignment, size_t size);
    • if (!powerof2(alignment) || (size % alignment) != 0): 判断对齐规则,如果不符合就置错误码并且返回nullptr;
    • return debug_memalign(alignment, size);: 调用debug_memalign;
void* debug_aligned_alloc(size_t alignment, size_t size) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled()) {
    return g_dispatch->aligned_alloc(alignment, size);
  }
  if (!powerof2(alignment) || (size % alignment) != 0) {
    errno = EINVAL;
    return nullptr;
  }
  return debug_memalign(alignment, size);
}

debug_posix_memalign

  • int debug_posix_memalign(void** memptr, size_t alignment, size_t size)
    • if (alignment < sizeof(void*) || !powerof2(alignment)): 判断对齐规则,如果不符合就返回EINVAL错误码;
    • *memptr = debug_memalign(alignment, size);: 调用debug_memalign;
    • return (*memptr != nullptr) ? 0 : ENOMEM;
int debug_posix_memalign(void** memptr, size_t alignment, size_t size) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled()) {
    return g_dispatch->posix_memalign(memptr, alignment, size);
  }

  if (alignment < sizeof(void*) || !powerof2(alignment)) {
    return EINVAL;
  }
  int saved_errno = errno;
  *memptr = debug_memalign(alignment, size);
  errno = saved_errno;
  return (*memptr != nullptr) ? 0 : ENOMEM;
}

debug_pvalloc

  • void* debug_pvalloc(size_t bytes);
    • size_t size = __BIONIC_ALIGN(bytes, pagesize);
    • if (size < bytes): 判断是否页对齐,否就置错误码ENOMEM,返回nullptr;
    • return debug_memalign(pagesize, size);: 调用debug_memalign;
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
void* debug_pvalloc(size_t bytes) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled()) {
    return g_dispatch->pvalloc(bytes);
  }

  size_t pagesize = getpagesize();
  size_t size = __BIONIC_ALIGN(bytes, pagesize);
  if (size < bytes) {
    // Overflow
    errno = ENOMEM;
    return nullptr;
  }
  return debug_memalign(pagesize, size);
}
...
#endif

debug_valloc

  • void* debug_valloc(size_t size);
    • return debug_memalign(getpagesize(), size);: 调用debug_memalign;
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
void* debug_valloc(size_t size) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled()) {
    return g_dispatch->valloc(size);
  }
  return debug_memalign(getpagesize(), size);
}
#endif

debug_free

  • void debug_free(void* pointer);
    • 加锁;
    • g_debug->record->AddEntry(new FreeEntry(pointer));: 若开启了RECORD_ALLOCS选项,则将pointer记录下来;
    • if (!VerifyPointer(pointer, "free")): 如果验证失败则直接退出debug_free,成功则调用InternalFree;
      • 首先会判断是否开启边界检查,若开启则通过pointer获取到Header的信息,通过对比TAG来查看是不是二次释放,如果是的话会将错误信息写入到一个缓存中,最终通过异步的方式发送到/dev/socket/logdwsocket, 也就是打印到系统日志中;
      • 若开启了TRACK_ALLOCS选项,会在pointer_哈希表中查找该段内存是否存在,若不存在就说明是unknown的内存,同样也将错误信息打印到日志中;
    • InternalFree(pointer)
      • (g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()
      • 如果开启了边界检查,则通过pointer拿到Header, 通过g_debug->front_guard->Valid(header)进而拿到guard的地址进行比对; 如果对比失败则向日志中记录该事件;
      • pointerHeaderTAG设置为DEBUG_FREE_TAG;
      • 若开启了FILL_ON_FREE, 则将pointer的内存全部置为0xef;
      • 若开启了TRACK_ALLOCS,将pointerpointers_哈希表中移除;
      • 若开启了FREE_TRACK, 将pointer的地址和大小添加到隔离区free_pointers_中,随后将pointer地址进行free;
void debug_free(void* pointer) {
  Unreachable::CheckIfRequested(g_debug->config());

  if (DebugCallsDisabled() || pointer == nullptr) {
    return g_dispatch->free(pointer);
  }
  ScopedConcurrentLock lock;
  ScopedDisableDebugCalls disable;
  ScopedBacktraceSignalBlocker blocked;

  if (g_debug->config().options() & RECORD_ALLOCS) {  // 是否开启 RECORD_ALLOCS
    g_debug->record->AddEntry(new FreeEntry(pointer));
  }

  if (!VerifyPointer(pointer, "free")) {
    return;
  }

  InternalFree(pointer);
}
static void InternalFree(void* pointer) {
  if ((g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
    debug_dump_heap(android::base::StringPrintf(
                        "%s.%d.txt", g_debug->config().backtrace_dump_prefix().c_str(), getpid())
                        .c_str());
  }

  void* free_pointer = pointer;
  size_t bytes;
  Header* header;
  if (g_debug->HeaderEnabled()) {
    // 开启边界检查
    header = g_debug->GetHeader(pointer);  // 拿到该段内存的Header
    free_pointer = header->orig_pointer;

    // 检查两个guard的数据是否被修改,修改就说明有越界行为
    if (g_debug->config().options() & FRONT_GUARD) {
      if (!g_debug->front_guard->Valid(header)) {
        g_debug->front_guard->LogFailure(header);
      }
    }
    if (g_debug->config().options() & REAR_GUARD) {
      if (!g_debug->rear_guard->Valid(header)) {
        g_debug->rear_guard->LogFailure(header);
      }
    }

    header->tag = DEBUG_FREE_TAG;

    bytes = header->usable_size;
  } else {
    bytes = g_dispatch->malloc_usable_size(pointer);
  }

  if (g_debug->config().options() & FILL_ON_FREE) {  // 是否开启 FILL_ON_FREE
    // 将free的内存填满 0xef
    size_t fill_bytes = g_debug->config().fill_on_free_bytes();
    fill_bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
    memset(pointer, g_debug->config().fill_free_value(), fill_bytes);
  }

  if (g_debug->TrackPointers()) {   // 是否开启 TRACK_ALLOCS
    PointerData::Remove(pointer);  // 因为是free事件,因此将该内存从 pointers_ 哈希表中删除
  }

  if (g_debug->config().options() & FREE_TRACK) {  // 是否开启 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.
    pointer = PointerData::AddFreed(pointer, bytes);  // 如果freepointers_ 的deque中还有"位置",则返回nullptr,否则返回的就是整个deque中"最老"的哪一个
    if (pointer != nullptr) {
      if (g_debug->HeaderEnabled()) {
        pointer = g_debug->GetHeader(pointer)->orig_pointer;
      }
      g_dispatch->free(pointer);   // 真正调用free释放内存
    }
  } else {
    // 不开启 FREE_TRACK 直接调用free
    g_dispatch->free(free_pointer);
  }
}
void* PointerData::AddFreed(const void* ptr, size_t size_bytes) {
  size_t hash_index = 0;
  size_t num_frames = g_debug->config().free_track_backtrace_num_frames();
  if (num_frames) {  // backtrace 的深度
    hash_index = AddBacktrace(num_frames, size_bytes);
  }

  void* last = nullptr;
  std::lock_guard<std::mutex> freed_guard(free_pointer_mutex_);
  if (free_pointers_.size() == g_debug->config().free_track_allocations()) {
    // free_pointers_ 数量到达边界
    // 拿出"最老"的free事件,拿出其内存地址
    FreePointerInfoType info(free_pointers_.front());
    free_pointers_.pop_front(); 
    VerifyFreedPointer(info);
    RemoveBacktrace(info.hash_index);
    last = reinterpret_cast<void*>(DemanglePointer(info.mangled_ptr));
  }

  // 将新来的free事件加入到free_pointers_中
  uintptr_t mangled_ptr = ManglePointer(reinterpret_cast<uintptr_t>(ptr));
  free_pointers_.emplace_back(FreePointerInfoType{mangled_ptr, hash_index});
  return last;
}

Reference

platform_bionic