Malloc Debug
Malloc debug的源码在platform_bionic的
libc\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 |
------------------------------------------------------------------------------------------------
| | | | | |
------------------------------------------------------------------------------------------------
算法描述:
建立边界:
- hook到malloc等事件的发生;
- 根据64位和32位的不同,来计算
Alignment,Header,真正存储数据内存大小以及front_guard和rear_guard大小,实际分配计算好的综合大小的内存; - 找到Header所在的内存的地址,计算并写入
tag,orig_pointer,size以及usable_size的数据; - 找到真正存储数据内存的地址,返回给调用者。
查看是否存在越界行为:
- hook到free事件的发生;
- 根据free传入的指针,来通过偏移拿到
Header的信息; - 通过
Header和偏移来拿到front_guard和rear_guard的地址; - 读取这两块地址的数据,通过与默认值对比来检查是否存在有越界行为;
细节描述:
1. Header结构体的作用
内存分配事件有malloc类,memalign类,对于malloc类事件而言,实际分配的内存大小就是size;而对于memalign类事件而言,实际分配的内存大小是根据alignment和bytes计算出来的。因此,要记录实际分配的内存大小和调用时输入的内存大小,就需要一个“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的初始化就变得十分简单:tag为DEBUG_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中的tag为DEBUG_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,只要要分配内存大小大于这个值,就视为分配内存过大,随即置errno为ENOMEM, 返回nullptr。
void *malloc(size_t size );
static size_t MaxSize() { return (1U << 31) - 1; }
if (size > PointerInfoType::MaxSize()) {
errno = ENOMEM;
return nullptr;
}
calloc溢出(calloc-overflow)
void *calloc(size_t num, size_t size);
calloc调用之前计算其num和size的乘积,也就是即将要分配内存的大小,若该大小通过__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, )
- 存在一个用于记录free事件的隔离区
free_pointers_,该隔离区默认可记录free事件的数量是100; - 若一个新的free事件到达,则将其内存的实际存储数据部分写满
0xef; - 当malloc debug结束运行时,遍历整个隔离区,判断隔离区内的所有free事件的实际存储数据部分是否全为
0xef,不是则说明该段内存在free之后还被使用;
校验案例: heap-use-after-free
二次释放(double-free)
源码地址:libc\malloc_debug\PointerData.cpp (PointerData::VerifyAllFreed, PointerData::AddFreed, PointerData::LogFreeBacktrace)
- 存在一个用于记录free事件的隔离区
free_pointers_,该隔离区默认可记录free事件的数量是100; - 若新到达的free事件已经存在于隔离区内,则判定为二次释放;
- 若新到达的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_initializedebug->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的地址进行比对; 如果对比失败则向日志中记录该事件; - 将
pointer的Header的TAG设置为DEBUG_FREE_TAG; - 若开启了
FILL_ON_FREE, 则将pointer的内存全部置为0xef; - 若开启了
TRACK_ALLOCS,将pointer从pointers_哈希表中移除; - 若开启了
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;
}