ptmalloc堆实现

概述

glibc-2.3.x. 之后,glibc 中集成了ptmalloc2

 

可以下载glibc源码查看ptmalloc

http://ftp.gnu.org/gnu/glibc/

 

查看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

理解ptmalloc堆最后的办法是查看相关资料后看源码。word文档拷贝的时候颜色丢失了,格式也有丢失。先这样。

API

2.1 其它

2.1.1 catomic_compare_and_exchange_val_acq

如果*MEM等于OLDVAL,则将*MEM存储为NEWVAL,返回OLDVAL

/* Atomically store NEWVAL in *MEM if *MEM is equal to OLDVAL.

Return the old *MEM value. */

#ifndef catomic_compare_and_exchange_val_acq

# ifdef __arch_c_compare_and_exchange_val_32_acq

# define catomic_compare_and_exchange_val_acq(mem, newval, oldval) \

__atomic_val_bysize (__arch_c_compare_and_exchange_val,acq,    \

     mem, newval, oldval)

# else

# define catomic_compare_and_exchange_val_acq(mem, newval, oldval) \

atomic_compare_and_exchange_val_acq (mem, newval, oldval)

# endif

#endif

2.1.2 catomic_compare_and_exchange_val_rel

如果*MEM等于OLDVAL,则将*MEM存储为NEWVAL,返回OLDVAL

#ifndef catomic_compare_and_exchange_val_rel

# ifndef atomic_compare_and_exchange_val_rel

# define catomic_compare_and_exchange_val_rel(mem, newval, oldval\

catomic_compare_and_exchange_val_acq (mem, newval, oldval)

# else

# define catomic_compare_and_exchange_val_rel(mem, newval, oldval\

atomic_compare_and_exchange_val_rel (mem, newval, oldval)

# endif

#endif

 

2.1.3 Perturb_byte

l perturb_byte:一个字节,用于初始化分配的内存,出于调试的目的。

l __libc_mallopt(int param_number, int value):设置perturb_byte

l alloc_perturb (char *p, size_t n):内存初始化为perturb_byte^0xff

l free_perturb (char *p, size_t n):内存初始化为perturb_byte

l do_set_perturb_byte (int32_t value):设置perturb_byte

 

static int perturb_byte;

 

static void

alloc_perturb (char *p, size_t n)

{

if (__glibc_unlikely (perturb_byte))

memset (p, perturb_byte ^ 0xff, n);

}

static void

free_perturb (char *p, size_t n)

{

if (__glibc_unlikely (perturb_byte))

memset (p, perturb_byte, n);

}

static inline int

__always_inline

do_set_perturb_byte (int32_t value)

{

LIBC_PROBE (memory_mallopt_perturb, 2, value, perturb_byte);

perturb_byte = value;

return 1;

}

int

__libc_mallopt (int param_number, int value){

//......

switch (param_number)

{

case M_PERTURB:

do_set_perturb_byte (value);

break;

}

//......

}

 

2.2 malloc_init_state

初始化arena header(malloc_state)结构:

初始化bins数组,构造bin双链表;

设置NONCONTIGUOUS_BIT标记;

设置fastbin用户数据的最大值:global_max_fast

设置FASTCHUNKS_BIT标记;

初始化top chunk

static void

malloc_init_state (mstate av)

{

int i;

mbinptr bin;

 

/* Establish circular links for normal bins */

for (i = 1; i < NBINS; ++i)

{

bin = bin_at (av, i);

bin->fd = bin->bk = bin;

}

#if MORECORE_CONTIGUOUS

if (av != &main_arena)

#endif

set_noncontiguous (av);

if (av == &main_arena)

set_max_fast (DEFAULT_MXFAST);

av->flags |= FASTCHUNKS_BIT;

av->top = initial_top (av);

}

2.3 Unlink

unlink 用来将一个双向 bin 链表中的一个 chunk 取出来

 

参数:

AVarena header(malloc_state)

P :将要unlinkchunk

BKP后面的chunk    <--

FDP前面的chunk    -->

3chunk的顺序

-------->地址增加

BK P FD

 

具体过程如下:

chunkFD/BK链表中摘除;

如果是large chunk,则将chunkfd_nextsize/bk_nextsize链表中摘除

 

/* Take a chunk off a bin list */

#define unlink(AV, P, BK, FD) { \

FD = P->fd;                \

BK = P->bk;                \

// 检查3个chunk的BK/FD链接是否一致

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))    \

malloc_printerr (check_action, "corrupted double-linked list", P, AV); \

else {                 \

// unlink P

FD->bk = BK;               \

BK->fd = FD;               \

// 如果P的大小不属于small bin

if (!in_smallbin_range (chunksize_nomask (P))      \

// P的fd_nextsize字段非空,即位于large bin中

// 这里期望fd_nextsize字段为NULL,即大概率为unsorted bin,小概率为large bin

&& __builtin_expect (P->fd_nextsize != NULL, 0)) {     \

// 检查3个chunk的fd_nextsize/bk_nextsize链接是否一致

      if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)   \

      || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \

      malloc_printerr (check_action,         \

         "corrupted double-linked list (not small)", \

         P, AV);           \

      // 此时P在fd_nextsize链表中

      // 如果FD不在fd_nextsize链表中,则将FD加入链表中

     if (FD->fd_nextsize == NULL) {         \

// 如果P链接到自身,则令FD也链接到自身

if (P->fd_nextsize == P)         \

FD->fd_nextsize = FD->bk_nextsize = FD;    \

else {               \

// 否则我们需要将 FD 插入到 nextsize 形成的双链表中

// 更新FD的fd_nextsize/bk_nextsize

// 更新P->fd_nextsize/P->bk_nextsize

FD->fd_nextsize = P->fd_nextsize;      \

FD->bk_nextsize = P->bk_nextsize;      \

P->fd_nextsize->bk_nextsize = FD;      \

P->bk_nextsize->fd_nextsize = FD;      \

}              \

} else {               \

// P和FD都在fd_nextsize链表中,将P从fd_nextsize链表中摘除

P->fd_nextsize->bk_nextsize = P->bk_nextsize;    \

P->bk_nextsize->fd_nextsize = P->fd_nextsize;    \

}                \

}                \

}                  \

}

 

 

1. __builtin_expect

函数__builtin_expect()GCC v2.96版本引入的, 其声明如下:

long __builtin_expect(long exp, long c);

参数

exp 为一个整型表达式, 例如: (ptr != NULL)

必须是一个编译期常量, 不能使用变量

返回值

等于 第一个参数 exp

功能

GCC 提供了这个内建函数来帮助程序员处理分支预测.

允许程序员将最有可能执行的分支告诉编译exp的值很可能是c

GCC在编译过程中,会将可能性更大的代码紧跟着前面的代码,从而减少指令跳转带来的性能上的下降, 达到优化程序的目的。(此时CPU流水线预取指令会起作用

2.4 Malloc

见注释,最终调用的是_int_malloc

strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc)

 

void *

__libc_malloc (size_t bytes)

{

mstate ar_ptr;

void *victim;

 

// 检查是否有内存分配钩子,如果有,调用钩子并返回

void *(*hook) (size_t, const void *)

= atomic_forced_read (__malloc_hook);

if (__builtin_expect (hook != NULL, 0))

return (*hook)(bytes, RETURN_ADDRESS (0));

 

//接着会寻找一个 arena 来试图分配内存

arena_get (ar_ptr, bytes);

 

//调用 _int_malloc 函数去申请对应的内存

victim = _int_malloc (ar_ptr, bytes);

 

//尝试再去寻找一个可用的 arena,并分配内存

/* Retry with another arena only if we were able to find a usable arena

before. */

if (!victim && ar_ptr != NULL)

{

LIBC_PROBE (memory_malloc_retry, 1, bytes);

ar_ptr = arena_get_retry (ar_ptr, bytes);

victim = _int_malloc (ar_ptr, bytes);

}

 

//unlock

if (ar_ptr != NULL)

__libc_lock_unlock (ar_ptr->mutex);

 

//判断目前的状态是否满足以下条件

//要么没有申请到内存

//要么是 mmap 的内存

//要么申请到的内存必须在其所分配的arena中

assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||

ar_ptr == arena_for_chunk (mem2chunk (victim)));

return victim;

}

libc_hidden_def (__libc_malloc)

 

2.5 _int_malloc

static void *

_int_malloc (mstate av, size_t bytes)

 

_int_malloc 是内存分配的核心函数,其核心思路有如下

1. Fast bin服务请求

2. Small bin服务请求

3. 如果是large chunk,则合并fast bin

4. 循环处理

1) Unsorted bin服务请求

(遍历的unsorted chunk要么匹配用户请求被返回,要么放入对应的bin中)

2) Large bin服务请求

3) next largest bin服务请求

4) top chunk服务请求

5) 合并fast bin,下次尝试

6) 从系统请求内存

 

具体过程如下:

1. 没有可用的arena,则使用sysmallocmmap获取chunk;

2. chunk大小属于fast bin,尝试从fast bin(单链表)链表头部获取;

3. chunk大小属于small bin,尝试从small bin(环形双链表)链表尾部获取;

4. chunk大小属于large bin,先合并 fast bin以解决分片问题;

5. 循环处理

这里使用外循环,是因为我们可能在处理中合并了一个满足要求的chunk,需要重新尝试。最多重新尝试一次,否则我们将扩展内存来服务请求。

l unsorted bin

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值