1 概述
在 glibc-2.3.x. 之后,glibc 中集成了ptmalloc2。
可以下载glibc源码查看ptmalloc
查看glibc版本
millionsky@ubuntu-16:~/tmp$ ldd --version ldd (Ubuntu GLIBC 2.23-0ubuntu9) 2.23 |
这里主要参考:
https://ctf-wiki.github.io/ctf-wiki/pwn/heap
本文参考的glibc源码是glibc-2.25.tar.xz
2 ptmalloc堆数据结构
2.1 Chunk
2.1.1 Malloc_chunk
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T mchunk_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; }; |
2.1.2 尺寸定义
#ifndef INTERNAL_SIZE_T
# define INTERNAL_SIZE_T size_t
#endif
/* The corresponding word size. */
#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
l INTERNAL_SIZE_T:size_t,32/64位整数
l SIZE_SZ:32/64
2.1.3 Chunk的对齐
/* 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 # define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \ ? __alignof__ (long double) : 2 * SIZE_SZ) #endif
/* The corresponding bit mask value. */ #define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) |
l MALLOC_ALIGNMENT:对齐字节,8/16
注意:如果64位上size_t为4字节,则对齐为8
l MALLOC_ALIGN_MASK:对齐掩码,0x7/0x0f
l 检查分配给用户的内存是否对齐
aligned_OK(m)判断m是否对齐;
misaligned_chunk(p)将p转换为对齐后的地址(向下取整);
/* 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)
2.1.4 最小chunk
l Chunk的最小尺寸:16/32
最小的chunk包含前面4个字段;
/* The smallest possible chunk */
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
注意:如果64位上size_t为4字节,则最小的chunk为24字节
l 最小malloc size
最小的malloc size是对齐后的最小chunk,大小为16/32
注意:如果64位上size_t为4字节,则MINSIZE为32。此时MIN_CHUNK_SIZE(24)和MINSIZE不一致。
/* The smallest size we can malloc is an aligned minimal chunk */
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
2.1.5 Malloc header<-->user pointer
Allocated chunk的前两个字段称为 chunk header/malloc header,后面的部分称为user data。转换只要移动chunk header即可,即8/16字节;
/* 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)) |
2.1.6 用户大小检查与转换
l REQUEST_OUT_OF_RANGE:判断请求字节是否超出范围
MINSIZE为16/32,
则最大的请求大小不能超过0xFFFF FFE0/0xFFFF FFFF FFFF FFC0
这里为了简化某些代码,边界制定得比较低,即时加上MINSIZE,也不好回绕0;
/* 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)) |
l request2size
Request2size:将用户请求的大小转换为对齐后的chunk大小
MALLOC_ALIGNMENT至少是2*SIZE_SZ,这里会补齐chunk header然后对齐。
/* 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); |
l checked_request2size:先检查范围再进行大小转换
#define checked_request2size(req, sz) \
if (REQUEST_OUT_OF_RANGE (req)) { \
__set_errno (ENOMEM); \
return 0; \
} \
(sz) = request2size (req);
2.1.7 标记相关
P标记:
/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1
/* extract inuse bit of previous chunk */
#define prev_inuse(p) ((p)->mchunk_size & PREV_INUSE)
M标记:
/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2
/* check for mmap()'ed chunk */
#define chunk_is_mmapped(p) ((p)->mchunk_size & IS_MMAPPED)
A标记
/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
from a non-main arena. This i