Base Glibc 2.41
------------------------- malloc_consolidate -------------------------
malloc_consolidate is a specialized version of free() that tears
down chunks held in fastbins. Free itself cannot be used for this
purpose since, among other things, it might place chunks back onto
fastbins. So, instead, we need to use a minor variant of the same
code.
主要用于将fastbin的堆块整理到unsortedbin中
在malloc过程中有两次机会进入这个函数
static void malloc_consolidate(mstate av)
{
mfastbinptr* fb; /* current fastbin being consolidated */
mfastbinptr* maxfb; /* last fastbin (for loop control) */
mchunkptr p; /* current chunk being consolidated */
mchunkptr nextp; /* next chunk to consolidate */
mchunkptr unsorted_bin; /* bin header */
mchunkptr first_unsorted; /* chunk to link to */
/* These have same use as in free() */
mchunkptr nextchunk;
INTERNAL_SIZE_T size;
INTERNAL_SIZE_T nextsize;
INTERNAL_SIZE_T prevsize;
int nextinuse;
atomic_store_relaxed (&av->have_fastchunks, false);
unsorted_bin = unsorted_chunks(av);
/*
Remove each chunk from fast bin and consolidate it, placing it
then in unsorted bin. Among other reasons for doing this,
placing in unsorted bin avoids needing to calculate actual bins
until malloc is sure that chunks aren't immediately going to be
reused anyway.
*/
maxfb = &fastbin (av, NFASTBINS - 1);
fb = &fastbin (av, 0); // 取fastbinY[0]
do {
p = atomic_exchange_acquire (fb, NULL);
if (p != NULL) { // 如果存在fastbin则遍历所有单链表
do {
{
if (__glibc_unlikely (misaligned_chunk (p))) // 校验堆快是否对齐
malloc_printerr ("malloc_consolidate(): "
"unaligned fastbin chunk detected");
unsigned int idx = fastbin_index (chunksize (p)); // 校验size
if ((&fastbin (av, idx)) != fb)
malloc_printerr ("malloc_consolidate(): invalid chunk size");
}
check_inuse_chunk(av, p); // 检查inuse位
nextp = REVEAL_PTR (p->fd); // fastbin->fd
/* Slightly streamlined version of consolidation code in free() */
size = chunksize (p);
nextchunk = chunk_at_offset(p, size); // next_chunk
nextsize = chunksize(nextchunk);
if (!prev_inuse(p)) { // 如果victim chunk的prev_inuse位为0
prevsize = prev_size (p); // 向前合并
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
if (__glibc_unlikely (chunksize(p) != prevsize)) // 注意size和prevsize需要一致
malloc_printerr ("corrupted size vs. prev_size in fastbins");
unlink_chunk (av, p);
}
if (nextchunk != av->top) {
nextinuse = inuse_bit_at_offset(nextchunk, nextsize); // next_chunk->inuse
if (!nextinuse) { // 向后合并
size += nextsize;
unlink_chunk (av, nextchunk);
} else
clear_inuse_bit_at_offset(nextchunk, 0);
first_unsorted = unsorted_bin->fd; // insert into unsortedbin
unsorted_bin->fd = p;
first_unsorted->bk = p;
if (!in_smallbin_range (size)) { // 如果合并后是largebin范围的chunk
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}
set_head(p, size | PREV_INUSE);
p->bk = unsorted_bin;
p->fd = first_unsorted;
set_foot(p, size);
}
else { // 被top_chunk合并
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
}
} while ( (p = nextp) != NULL);
}
} while (fb++ != maxfb);
}
主要逻辑是遍历所有fastbinY数组中的单链表
对每个fastbin节点首先校验堆块是否对齐,以及size是否是这个fastbinY索引的size
之后会校验这个chunk的inuse位
后续就是向前合并和向后合并的过程
通过判断自身chunk的preinuse位来判断前对快是否需要合并,如果需要合并还会去判断presize是否等于前堆块的size
向后合并会判断next_chunk是否是top_chunk,如果不是则会去判断next_chunk的inuse位,这里因为chunk是fastbin范围内的,所以不会存在presize和size的校验,之后将合并后的chunk判断大小初始化fd_nextsize以及bk_nextsize并链入unsortedbin中
如果next_chunk是top_chunk则会被top_chunk合并
如果能够挟持fastbin的fd
需满足: