Heap 0x01
栈都没学明白 比赛都没打明白 然后开始学更不明不白的堆..
以下内容参考CTF wiki和学长/大佬(其实主要就是a3师傅)的博客,融合了一点学的时候的个人理解,权当是学习的笔记吧。
可能逻辑比较混乱,希望会有大佬给我指出一些逻辑上或者实际知识层面的一些问题吧OvO
HEAP
在程序运行过程中,堆可以提供动态分配的内存,允许程序申请大小未知的内存。堆其实就是程序虚拟地址空间的一块连续的线性区域,它由低地址向高地址方向增长。我们一般称管理堆的那部分程序为堆管理器。
这个堆管理器,主要在做两件事:
- 响应用户的请求并申请内存
- 管理用户释放的内存
目前linux系统下使用的堆分配器是glibc的堆分配器:ptmalloc2,使用malloc和free来分配/释放内存。
我个人在这的理解是,堆(这个内存区域)在预先就已经被操作系统划分出来了,但是分配出来的一大块只是一个虚拟的部分,只有当我们使用到真正相应的内存的时候,才会给到我们物理意义上的内存。
所以这个划分的大堆块(这个内存区域本身),称为arena,arena有主线程和其他线程的区分,主线程的叫做main arena,通过sbrk创建,其他线程通过mmap创建。
0x01 堆的基本操作
我在这里就首先放一下malloc和free这两个最基本的堆操作吧OvO
以下是基于glibc2.23 ptmalloc的源码,我后续也找了这么一个网站去自己啃了一下malloc.c - malloc/malloc.c - Glibc source code (glibc-2.23) - Bootlin
(其实写这个博客的时候很多都是一边看a3师傅的博客和CTF wiki然后看看源码然后摘抄笔记的。。)
malloc
malloc(size_t n)
/*
Returns a pointer to a newly allocated chunk of at least n bytes,
or null if no space is available.
Additionally, on failure, errno is set to ENOMEM on ANSI C systems.
If n is zero, malloc returns a minumum-sized chunk.
(The minimum size is 16 bytes on most 32bit systems, and 24 or 32 bytes on 64bit systems.)
On most systems, size_t is an unsigned type,
so calls with negative arguments are interpreted as requests for huge amounts of space,
which will often fail. The maximum supported value of n differs across systems,
but is in all cases less than the maximum representable value of a size_t.
*/
综上,malloc的用处是返回一个指针,这个指针指向新分配的内存块(这个内存块一般称为chunk)的起始地址,如果分配失败返回null,同时malloc在这个说明中也对分配异常做了一些说明:
- 如果n=0,返回当前系统允许的堆的最小内存块。
- 如果n为负数的话,因为size_t这个东西(后续会有说明这个宏)是一个无符号数的类型,所以程序会申请一个很大的内存块,但通常来说都会失败(O。o)
free
free(void* p)
/*
Releases the chunk of memory pointed to by p,
that had been previously allocated using malloc or a related routine such as realloc.
It has no effect if p is null.
It can have arbitrary (i.e., bad!) effects
if p has already been freed.
Unless disabled (using mallopt),
freeing very large spaces will when possible,
automatically trigger operations that give back unused memory to the system,
thus reducing program footprint.
*/
综上,free的用处是释放p所指向的内存块,这个内存块的来源可能是malloc或realloc。
同理,free也对异常情况进行了一些处理:
- 当p为空指针时,无操作
- 当p已经被释放之后,再次释放会出现一些奇妙的效果(double free)
0x02 malloc_chunk及有关宏
这部分参考了一下a3师傅博客的那个逻辑,先写一下chunk的结构和有关的宏
malloc_chunk
非常有意思的是,无论一个 chunk 的大小如何,处于分配状态还是释放状态,它们都使用一个统一的结构。
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
有关的宏
#ifndef INTERNAL_SIZE_T
#define INTERNAL_SIZE_T size_t
#endif
/* The corresponding word size */
#define SIZE_SZ (sizeof(INTERNAL_SIZE_T))
/*
MALLOC_ALIGNMENT is the minimum alignment for malloc'ed chunks.
It must be a power of two at least 2 * SIZE_SZ, even on machines
for which smaller alignments would suffice. It may be defined as
larger than this though. Note however that code and data structures
are optimized for the case of 8-byte alignment.
*/
#ifndef MALLOC_ALIGNMENT
# if !SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_16)
/* This is the correct definition when there is no past ABI to constrain it.
Among configurations with a past ABI constraint, it differs from
2*SIZE_SZ only on powerpc32. For the time being, changing this is
causing more compatibility problems due to malloc_get_state and
malloc_set_state than will returning blocks not adequately aligned for
long double objects under -mlong-double-128. */
# define MALLOC_ALIGNMENT (2 *SIZE_SZ < __alignof__ (long double)
\ ? __alignof__ (long double) : 2 *SIZE_SZ)
# else
# define MALLOC_ALIGNMENT (2 *SIZE_SZ)
# endif
#endif
/* The corresponding bit mask value */
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
-
INTERNAL_SIZE_T其实就是size_t(上文提到的宏),32位下为4字节,64位下为8字节
-
SIZE_SZ是上面那个宏的别名
-
MALLOC_ALIGNMENT则定义了chunk在内存中对齐的字节数,一般来说算出来都是对2*SIZE_SZ对齐,即32位下8字节对齐,64位下16字节对齐
还有一个宏的用处好像很奇怪,我没搜到,也没总结出来(X_X)后续再补
接下来解释一下malloc_chunk结构体中的各种变量:
- prev_size:用以保存前一个内存物理地址相邻的chunk的size,仅在该chunk为free状态时会被使用到
- size:顾名思义,用以保存这个chunk的总的大小,即同时包含chunk头(prev_size + size)和chunk剩余部分的大小
- fd&&bk:仅在在chunk被free后使用,用以连接其他的chunk,也就是说当chunk处于被使用的状态时该字段无效,被用以存储用户的数据
- fd_nextsize&&bk_nextsize:仅在在chunk被free后使用,用以连接其他的chunk,与上面那个唯一不同的就是这二位只用于比较大的free的chunk
插一个关于size的后三位的一点点东西,由上面这些介绍可知size的值必须是2*SIZE_SZ的整数倍,所以对于size这个东西来说低3/4位(32/64位系统)将会永远置0(根据二进制知识可知),所以size的后三位就被用来保存这个chunk的三个状态:
- NON_MAIN_ARENA(A):表示该 chunk 属于主分配区(1)或者非主分配区(0)
- IS_MAPPED(M):记录当前 chunk 是否是由 mmap 分配的,M 为 1 表示该 chunk 是从 mmap 映射区域分配的,否则是从 heap 区域分配的
- PREV_INUSE(P):记录前一个 chunk 块是否被分配。一般来说,堆中第一个被分配的内存块的 size 字段的 P 位都会被设置为 1,以便于防止访问前面的非法内存。当一个 chunk 的 size 的 P 位为 0 时,我们能通过 prev_size 字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲 chunk 之间的合并。
讲完这些之后,放两张图来更清晰的表达一下使用中和空闲中的chunk
使用中的chunk

我们称前两个字段(即prev_size&size)为chunk header,后面mem指向的部分称为user data。
每次 malloc 申请得到的内存指针,其实指向 user data 的起始处。
当一个 chunk 处于使用状态时,它的下一个 chunk 的 prev_size 域无效,所以下一个 chunk 的该部分也可以被当前 chunk 使用。这就是 chunk 中的空间复用。
空闲中的chunk

这些被释放的chunk根据fd和bk两个指针形成了一个链表。
一般情况下,物理相邻的两个空闲 chunk 会被合并为一个 chunk 。堆管理器会通过 prev_size 字段以及 size 字段合并两个物理相邻的空闲 chunk 块。
然后再是..
chunk相关的宏
/*
---------- Size and alignment checks and conversions ----------
*/
/* conversion from malloc headers to user pointers, and back */
#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ))
#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ))
/* The smallest possible chunk */
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
/* The smallest size we can malloc is an aligned minimal chunk */
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
/* Check if m has acceptable alignment */
#define aligned_OK(m) (((unsigned long)(m) & MALLOC_ALIGN_MASK) == 0)
#define misaligned_chunk(p) \
((uintptr_t)(MALLOC_ALIGNMENT == 2 * SIZE_SZ ? (p) : chunk2mem (p)) \
& MALLOC_ALIGN_MASK)
总结一下是这些:
- chunk2mem:将指向chunk的指针转为指向memory的指针,即指针由指向chunk header(刚好是prev_size的位置)移动到指向fd
- mem2chunk:将指向memory的指针转换为指向chunk的指针,即指针由指向fd移动到指向chunk header,与chunk2mem互为逆操作
- MIN_CHUNK_SIZE:所可分配的最小的chunk的size,为由prev_size到fd_nextsize的偏移量,32位下是0x10,64位下是0x20,即一个最小的chunk应当包含有chunk header和fd、bk
- MINSIZE:所能分配的最小chunk的size;我们所能分配的最小的size的chunk应当为一个内存对其的最小的chunk,这里算出来其实就是MIN_CHUNK_SIZE,目的不明(我个菜鸡也不好猜测)
- aligned_OK:该宏用以检查是否与MALLOC_ALIGNMENT对齐,因为若是与MALLOC_ALIGNMENT不对其的话,其与MALLOC_ALIGN_MASK进行与运算的结果必然不为0(这里应该就不需要我再说明为什么了…)
- misaligned_chunk:该宏用以检测一个chunk是否为未对齐的chunk,若是则返回未对齐的字节数
还有memory request相关的宏..用来对请求的堆块的大小进行检查的
(这一部分的宏几乎完全是照搬了a3师傅的博客的,只能说a3师傅真的太厉害了捏)
/*
Check if a request is so large that it would wrap around zero when
padded and aligned. To simplify some other code, the bound is made
low enough so that adding MINSIZE will also not wrap around zero.
*/
#define REQUEST_OUT_OF_RANGE(req) \
((unsigned long) (req) >= \
(unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE))
/* pad request bytes into a usable size -- internal version */
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
/* Same, except also perform argument check */
#define checked_request2size(req, sz) \
if (REQUEST_OUT_OF_RANGE (req)) { \
__set_errno (ENOMEM); \
return 0; \
} \
(sz) = request2size (req);
大致如下:
- REQUEST_OUT_OF_RANGE:宏如其名,用以检查用户请求的内存大小是否大于规定的最大内存大小,这个值被定义为4字节整型能表示的最大值再减去两个最小chunk的size所求得的值,为的是避免当有人真的尝试去分配了这样的一个大chunk时malloc内部将这个值加上MINSIZE后会发生整型上溢而变成0附近的一个较小的值
- request2size:作用是将用户请求的内存大小转换为内存对齐的大小,这个转换后的大小便是实际分配给用户的chunk的size;若是请求的内存大小小于MINSIZE得到的size直接为MINSIZE,否则则会得到一个与MALLOC_ALIGNMENT对齐的chunk size
- checked_request2size:可以理解为request2size宏封装上了size检查的宏,需要注意的是若是REQUEST_OUT_OF_RANGE则该宏则会在设置errno标志位为ENOMEM后直接return 0,终止其调用函数的执行
然后还有一些其他的,这些是管理那个size标志位的,暂时我不知道有什么用(或者感觉像是一个额外的一部分,不知道对后续的利用有什么作用那种感觉)但是看见a3师傅博客里整整齐齐的样子的话我也放到这里吧,预防他说的“后续需要递归回来反复看这些宏”的情况orz..
/*
Bits to mask off when extracting size
Note: IS_MMAPPED is intentionally not masked off from size field in
macros for which mmapped chunks should never be seen. This should
cause helpful core dumps to occur if it is tried by accident by
people extending or adapting this malloc.
*/
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
/* Get size, ignoring use bits */
#define chunksize(p) ((p)->size & ~(SIZE_BITS))
/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))
/* Ptr to previous physical malloc_chunk */
#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - ((p)->prev_size)))
/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))
/* extract p's inuse bit */
#define inuse(p) \
((((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))->size) & PREV_INUSE)
/* set/clear chunk as being inuse without otherwise disturbing */
#define set_inuse(p) \
((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))->size |= PREV_INUSE
#define clear_inuse(p) \
((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))->size &= ~(PREV_INUSE)
/* check/set/clear inuse bits in known places */
#define inuse_bit_at_offset(p, s) \
(((mchunkptr) (((char *) (p)) + (s)))->size & PREV_INUSE)
#define set_inuse_bit_at_offset(p, s) \
(((mchunkptr) (((char *) (p)) + (s)))->size |= PREV_INUSE)
#define clear_inuse_bit_at_offset(p, s) \
(((mchunkptr) (((char *) (p)) + (s)))->size &= ~(PREV_INUSE))
/* Set size at head, without disturbing its use bit */
#define set_head_size(p, s) ((p)->size = (((p)->size & SIZE_BITS) | (s)))
/* Set size/use field */
#define set_head(p, s) ((p)->size = (s))
/* Set size at footer (only when chunk is not in use) */
#define set_foot(p, s) (((mchunkptr) ((char *) (p) + (s)))->prev_size = (s))
大致如下:
- SIZE_BITS:该宏用以表示三个标志位全为1
- chunksize ():该宏用以清除size字段的标志位,得到原始的chunk size
- next_chunk ():该宏用以获得指向该chunk的物理相邻的下一个chunk的指针
- prev_chunk ():该宏用以获得指向该chunk的物理相邻的上一个chunk的指针,需要注意的是prev_size字段必须有效,即上一个chunk必须为free状态
- chunk_at_offset ():该宏用以将ptr + offset位置作为一个chunk来对待,获得一个指向ptr + offset位置的chunk指针(大意是假设ptr + offset上有一个chunk,然后我们用这个宏得到指向这个chunk的chunk指针,后面讲到top_chunk时会用到)
- inuse()、set_inuse()、clear_inuse ():检测、设置、清除chunk的PREV_INUSE标志位
- inuse_bit_at_offset()、set_inuse_bit_at_offset()、clear_inuse_bit_at_offset ():将ptr + offset位置视为一个chunk并检测、设置、清除该chunk的PREV_INUSE标志位
- set_head_size ():该宏用以在不改变标志位的情况下改变一个chunk的size字段
- set_size ():该宏用以改变一个chunk的size字段,是前者的简化版本
- set_foot ():该宏用以在将ptr + s视为一个chunk的地址的情况下将这个chunk的prev_size字段置为s
(感谢a3师傅的耐心整理 我爱a3)
再下一个 轮到..
0x03 Bins
上面好像有意无意的透露过我们free掉的chunk不会马上还给系统,ptmalloc会根据这些chunk里size的不同将之存放到不同的bins中进行统一的调度,目的是避免频繁的系统调用与降低内存分配的开销
下面是源码里对于bins的说明:
/*
Bins
An array of bin headers for free chunks. Each bin is doubly
linked. The bins are approximately proportionally (log) spaced.
There are a lot of these bins (128). This may look excessive, but
works very well in practice. Most bins hold sizes that are
unusual as malloc request sizes, but are more usual for fragments
and consolidated sets of chunks, which is what these bins hold, so
they can be found quickly. All procedures maintain the invariant
that no consolidated chunk physically borders another one, so each
chunk in a list is known to be preceeded and followed by either
inuse chunks or the ends of memory.
Chunks in bins are kept in size order, with ties going to the
approximately least recently used chunk. Ordering isn't needed
for the small bins, which all contain the same-sized chunks, but
facilitates best-fit allocation for larger chunks. These lists
are just sequential. Keeping them in order almost never requires
enough traversal to warrant using fancier ordered data
structures.
Chunks of the same size are linked with the most
recently freed at the front, and allocations are taken from the
back. This results in LRU (FIFO) allocation order, which tends
to give each chunk an equal opportunity to be consolidated with
adjacent freed chunks, resulting in larger free chunks and less
fragmentation.
To simplify use in double-linked lists, each bin header acts
as a malloc_chunk. This avoids special-casing for headers.
But to conserve space and improve locality, we allocate
only the fd/bk pointers of bins, and then use repositioning tricks
to treat these as the fields of a malloc_chunk*.
*/
typedef struct malloc_chunk *mbinptr;
大意在说bins是一个数组,chunk按照size的不同存放在不同下标的bin中,这些bin中的chunks都是使用双向链表的形式进行链接的。
ptmalloc会根据空闲的这些chunk的大小和使用状态将chunk分为4类:fast bins,small bins,large bins,unsorted bins。
对于其中除fastbins的部分,ptmalloc将它们维护在同一个数组中,这个数组的定义在malloc_state中(后续应该肯定会说到这个东西的)如下
#define NBINS 128
/* Normal bins packed as described above */
mchunkptr bins[ NBINS * 2 - 2 ];
在这个数组中,大概情况是这样的(图从CTFwiki里截的)

大概讲的就是在这个数组里面通过这个复用(大概和上面的复用同样的原理)用来节省空间
(什么都节省只会害了你)
0x04 各种bin与这些bin的宏
首先是bins通用的宏
/* addressing -- note that bin_at(0) does not exist */
//这个其实具体分析一下就是上面说过的复用的功能的实现
#define bin_at(m, i) \
(mbinptr) (((char *) &((m)->bins[((i) - 1) * 2])) \
- offsetof (struct malloc_chunk, fd))
/* analog of ++bin */
//这个是获取下一个bin的地址
#define next_bin(b) ((mbinptr) ((char *) (b) + (sizeof (mchunkptr) << 1)))
/* Reminders about list directionality within bins */
//以下宏用来遍历bin
#define first(b) ((b)->fd)
#define last(b) ((b)->bk)
...
#define bin_index(sz) \
((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))
然后在讲这几个普通的(也就是在数组里的这些)bins之前,先记一下算是介于这些之外的一个bin
Fastbins
这些是给一些比较小的内存块的申请需求的,摘抄一下CTFwiki中的介绍:
大多数程序经常会申请以及释放一些比较小的内存块。如果将一些较小的 chunk 释放之后发现存在与之相邻的空闲的 chunk 并将它们进行合并,那么当下一次再次申请相应大小的 chunk 时,就需要对 chunk 进行分割,这样就大大降低了堆的利用效率。因为我们把大部分时间花在了合并、分割以及中间检查的过程中。因此,ptmalloc 中专门设计了 fast bin,对应的变量就是 malloc state 中的 fastbinsY
/*
Fastbins
An array of lists holding recently freed small chunks. Fastbins
are not doubly linked. It is faster to single-link them, and
since chunks are never removed from the middles of these lists,
double linking is not necessary. Also, unlike regular bins, they
are not even processed in FIFO order (they use faster LIFO) since
ordering doesn't much matter in the transient contexts in which
fastbins are normally used.
Chunks in fastbins keep their inuse bit set, so they cannot
be consolidated with other free chunks. malloc_consolidate
releases all chunks in fastbins and consolidates them with
other free chunks.
*/
typedef struct malloc_chunk *mfastbinptr;
/*
This is in malloc_state.
/* Fastbins */
mfastbinptr fastbinsY[ NFASTBINS ];
*/
fastbin中的每个bin采取LIFO策略,也就是说,当用户需要的 chunk 的大小小于 fastbin 的最大大小时, ptmalloc 会首先判断 fastbin 中相应的 bin 中是否有对应大小的空闲块,如果有的话,就会直接从这个 bin 中获取 chunk。如果没有的话,ptmalloc 才会做接下来的一系列操作。
在默认情况下,32位的fastbin中最大的chunk为0x40,64位为0x80,超过这个范围的chunk在free之后会被送入unsorted bin中
说完这个fastbins,开始说这几个bins之前,我要先放一下↓
bins相关定义宏
/*
Indexing
Bins for sizes < 512 bytes contain chunks of all the same size, spaced
8 bytes apart. Larger bins are approximately logarithmically spaced:
64 bins of size 8
32 bins of size 64
16 bins of size 512
8 bins of size 4096
4 bins of size 32768
2 bins of size 262144
1 bin of size what's left
There is actually a little bit of slop in the numbers in bin_index
for the sake of speed. This makes no difference elsewhere.
The bins top out around 1MB because we expect to service large
requests via mmap.
Bin 0 does not exist. Bin 1 is the unordered list; if that would be
a valid chunk size the small bins are bumped up one.
*/
#define NBINS 128
bin0不存在,宏定义NBINS是128,所以实质上只有127个bin,其中bin1是unsorted bin.
small bins
由前面可以知道,从bin2开始应该是一些内存空间较小,且公差是8的一些chunk。且这些bin对应的链表采取FIFO的规则。
这里放出有关的宏(md好多 堆好难)
#define NSMALLBINS 64
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
// 是否需要对small bin的下标进行纠正
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
#define MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
//判断chunk的大小是否在small bin范围内
#define in_smallbin_range(sz) \
((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
// 根据chunk的大小得到small bin对应的索引。
#define smallbin_index(sz) \
((SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) \
: (((unsigned) (sz)) >> 3)) + \
SMALLBIN_CORRECTION)
- NSMALLBINS:small bins位于bins下标的终止,虽然说这个地方是64但是实际上他的意思并不是说small bins有62个
- SMALLBIN_WIDTH:每个 small bin 之间所储存的chunk的size差值(其实就是上面说的内存的公差,32位是8,64位16)
- MIN_LARGE_SIZE:最小的large bin范围内的chunk的size,只有小于这个size的chunk才属于small bin的范围内,32位下为512,64位下为1024
- in_smallbin_range ():用以检查一个chunk的size是否属于small bin范围内
- smallbin_index ():用以获得一个small bin chunk处在bin中的index,由于bin 1是unsorted bin所以直接进行移位操作即可获得index
通过MIN_LARGE_SIZE我们可以知道最大的small bin的size在32位下为504, 64位下为1008(减去了一个MALLOC_ALIGNMENT),由此我们便可以得出small bins的数量为62(没太懂这部分弯弯绕绕的意思,感觉其实也没差吧..?)
large bins
根据上面那个宏和small bins,自然而然的,下一个大一点的就该轮到large bins
公差及数量参见上面的宏,这我不写了,总之有6组63个
unsorted bin
/*
Unsorted chunks
All remainders from chunk splits, as well as all returned chunks,
are first placed in the "unsorted" bin. They are then placed
in regular bins after malloc gives them ONE chance to be used before
binning. So, basically, the unsorted_chunks list acts as a queue,
with chunks being placed on it in free (and malloc_consolidate),
and taken off (to be either used or placed in bins) in malloc.
The NON_MAIN_ARENA flag is never set for unsorted chunks, so it
does not have to be taken into account in size comparisons.
*/
说明是这样的,似乎可以视为free的chunk回归其所属的bin之前的缓冲区
有点不想写后续了,堆的一些基础就先当结了放在这篇里面,然后开一个新的去写堆溢出那些漏洞和漏洞利用啥的,虽然其实实际上目的是为了追一下进度..

浙公网安备 33010602011771号