学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1157

主题
发表于 2020-9-2 09:47:37 | 查看: 3883| 回复: 0

相关题目:

__libc_malloc
malloc 函数真正调用的是 __libc_malloc 函数。而__libc_malloc 函数只是用来简单封装 _int_malloc 函数。
该函数会首先检查是否有内存分配函数的钩子函数(__malloc_hook),这个主要用于用户自定义的堆分配函数,方便用户快速修改堆分配函数并进行测试。
这里需要注意的是,用户申请的字节一旦进入申请内存函数中就变成了无符号整数
// wapper for int_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);
如果分配失败的话,ptmalloc 会尝试再去寻找一个可用的 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);
  }

如果申请到了 arena,那么在退出之前还得解锁。
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;}

申请内存块

申请内存块
_int_malloc
在进入该函数后,函数立马定义了一系列自己需要的变量,并将用户申请的内存大小转换为内部的 chunk 大小。
static void *_int_malloc(mstate av, size_t bytes) {
  INTERNAL_SIZE_T nb;  /* normalized request size */
  unsigned int    idx; /* associated bin index */
  mbinptr         bin; /* associated bin */
  mchunkptr       victim;       /* inspected/selected chunk */
  INTERNAL_SIZE_T size;         /* its size */
  int             victim_index; /* its bin index */
  mchunkptr     remainder;      /* remainder from a split */
  unsigned long remainder_size; /* its size */
  unsigned int block; /* bit map traverser */
  unsigned int bit;   /* bit map traverser */
  unsigned int map;   /* current word of binmap */
  mchunkptr fwd; /* misc temp for linking */
  mchunkptr bck; /* misc temp for linking */
  const char *errstr = NULL;
  /*
  Convert request size to internal form by adding SIZE_SZ bytes
  overhead plus possibly more to obtain necessary alignment and/or
  to obtain a size of at least MINSIZE, the smallest allocatable
  size. Also, checked_request2size traps (returning 0) request sizes
  that are so large that they wrap around zero when padded and
  aligned.
  */
  checked_request2size(bytes, nb);

arena
    /* There are no usable arenas.  Fall back to sysmalloc to get a chunk from
  mmap.  */
  if (__glibc_unlikely(av == NULL)) {
  void *p = sysmalloc(nb, av);
  if (p != NULL) alloc_perturb(p, bytes);
  return p;
  }

fast bin
如果申请的 chunk 的大小位于 fastbin 范围内,需要注意的是这里比较的是无符号整数此外,是从 fastbin 的头结点开始取 chunk。(先进后出)
    /*
  If the size qualifies as a fastbin, first check corresponding bin.
  This code is safe to execute even if av is not yet initialized, so we
  can try it without checking, which saves some time on this fast path.
  */
  if ((unsigned long) (nb) <= (unsigned long) (get_max_fast())) {
  // 得到对应的fastbin的下标
  idx             = fastbin_index(nb);
  // 得到对应的fastbin的头指针
  mfastbinptr *fb = &fastbin(av, idx);
  mchunkptr    pp = *fb;
  // 利用fd遍历对应的bin内是否有空闲的chunk块,
  do {
  victim = pp;
  if (victim == NULL) break;
  } while ((pp = catomic_compare_and_exchange_val_acq(fb, victim->fd,
  victim)) != victim);
  // 存在可以利用的chunk
  if (victim != 0) {
  // 检查取到的 chunk 大小是否与相应的 fastbin 索引一致。
  // 根据取得的 victim ,利用 chunksize 计算其大小。
  // 利用fastbin_index 计算 chunk 的索引。
  if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0)) {
  errstr = "malloc(): memory corruption (fast)";
  errout:
  malloc_printerr(check_action, errstr, chunk2mem(victim), av);
  return NULL;
  }
  // 细致的检查。。只有在 DEBUG 的时候有用
  check_remalloced_chunk(av, victim, nb);
  // 将获取的到chunk转换为mem模式
  void *p = chunk2mem(victim);
  // 如果设置了perturb_type, 则将获取到的chunk初始化为 perturb_type ^ 0xff
  alloc_perturb(p, bytes);
  return p;
  }
  }

small bin
如果获取的内存块的范围处于 small bin 的范围,那么执行如下流程
    /*
  If a small request, check regular bin.  Since these "smallbins"
  hold one size each, no searching within bins is necessary.
  (For a large request, we need to wait until unsorted chunks are
  processed to find best fit. But for small ones, fits are exact
  anyway, so we can check now, which is faster.)
  */
  if (in_smallbin_range(nb)) {
  // 获取 small bin 的索引
  idx = smallbin_index(nb);
  // 获取对应 small bin 中的 chunk 指针
  bin = bin_at(av, idx);
  // 先执行 victim = last(bin),获取 small bin 的最后一个 chunk
  // 如果 victim = bin ,那说明该 bin 为空。
  // 如果不相等,那么会有两种情况
  if ((victim = last(bin)) != bin) {
  // 第一种情况,small bin 还没有初始化。
  if (victim == 0) /* initialization check */
  // 执行初始化,将 fast bins 中的 chunk 进行合并
  malloc_consolidate(av);
  // 第二种情况,small bin 中存在空闲的 chunk
  else {
  // 获取 small bin 中倒数第二个 chunk 。
  bck = victim->bk;
  // 检查 bck->fd 是不是 victim,防止伪造
  if (__glibc_unlikely(bck->fd != victim)) {
  errstr = "malloc(): smallbin double linked list corrupted";
  goto errout;
  }
  // 设置 victim 对应的 inuse 位
  set_inuse_bit_at_offset(victim, nb);
  // 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来
  bin->bk = bck;
  bck->fd = bin;
  // 如果不是 main_arena,设置对应的标志
  if (av != &main_arena) set_non_main_arena(victim);
  // 细致的检查,非调试状态没有作用
  check_malloced_chunk(av, victim, nb);
  // 将申请到的 chunk 转化为对应的 mem 状态
  void *p = chunk2mem(victim);
  // 如果设置了 perturb_type , 则将获取到的chunk初始化为 perturb_type ^ 0xff
  alloc_perturb(p, bytes);
  return p;
  }
  }
  }

large bin
当 fast bin、small bin 中的 chunk 都不能满足用户请求 chunk 大小时,就会考虑是不是 large bin。
但是,其实在 large bin 中并没有直接去扫描对应 bin 中的 chunk,而是先利用 malloc_consolidate函数处理 fast bin 中的 chunk,将有可能能够合并的 chunk 先进行合并后放到 unsorted bin 中,不能够合并的就直接放到 unsorted bin 中,然后再在下面的大循环中进行相应的处理。
为什么不直接从相应的 bin 中取出 large chunk 呢?这是 ptmalloc 的机制,它会在分配 large chunk 之前对堆中碎片 chunk 进行合并,以便减少堆中的碎片。
    /*
  If this is a large request, consolidate fastbins before continuing.
  While it might look excessive to kill all fastbins before
  even seeing if there is space available, this avoids
  fragmentation problems normally associated with fastbins.
  Also, in practice, programs tend to have runs of either small or
  large requests, but less often mixtures, so consolidation is not
  invoked all that often in most programs. And the programs that
  it is called frequently in otherwise tend to fragment.
  */
  else {
  // 获取large bin的下标。
  idx = largebin_index(nb);
  // 如果存在fastbin的话,会处理 fastbin
  if (have_fastchunks(av)) malloc_consolidate(av);
  }

大循环-遍历unsorted bin
如果程序执行到了这里,那么说明 与 chunk 大小正好一致的 bin (fast bin, small bin) 中没有 chunk 可以直接满足需求 ,但是 large chunk 则是在这个大循环中处理
在接下来的这个循环中,主要做了以下的操作
  • 按照 FIFO 的方式逐个将 unsorted bin 中的 chunk 取出来
  • 如果是 small request,则考虑是不是恰好满足,是的话,直接返回。
  • 如果不是的话,放到对应的 bin 中。
  • 尝试从 large bin 中分配用户所需的内存
unsorted bin遍历
先考虑 unsorted bin,再考虑 last remainder ,但是对于 small bin chunk 的请求会有所例外。
注意 unsorted bin 的遍历顺序为 bk。
        // 如果 unsorted bin 不为空
  // First In First Out
  while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) {
  // victim 为 unsorted bin 的最后一个 chunk
  // bck 为 unsorted bin 的倒数第二个 chunk
  bck = victim->bk;
  // 判断得到的 chunk 是否满足要求,不能过小,也不能过大
  // 一般 system_mem 的大小为132K
  if (__builtin_expect(chunksize_nomask(victim) <= 2 * SIZE_SZ, 0) ||
  __builtin_expect(chunksize_nomask(victim) > av->system_mem, 0))
  malloc_printerr(check_action, "malloc(): memory corruption",
  chunk2mem(victim), av);
  // 得到victim对应的chunk大小。
  size = chunksize(victim);

SMALL REQUESTs
            /*
  If a small request, try to use last remainder if it is the
  only chunk in unsorted bin.  This helps promote locality for
  runs of consecutive small requests. This is the only
  exception to best-fit, and applies only when there is
  no exact fit for a small chunk.
  */
  if (in_smallbin_range(nb) && bck == unsorted_chunks(av) &&
  victim == av->last_remainder &&
  (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {
  /* split and reattach remainder */
  // 获取新的 remainder 的大小
  remainder_size          = size - nb;
  // 获取新的 remainder 的位置
  remainder               = chunk_at_offset(victim, nb);
  // 更新 unsorted bin 的情况
  unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder;
  // 更新 av 中记录的 last_remainder
  av->last_remainder                                = remainder;
  // 更新last remainder的指针
  remainder->bk = remainder->fd = unsorted_chunks(av);
  if (!in_smallbin_range(remainder_size)) {
  remainder->fd_nextsize = NULL;
  remainder->bk_nextsize = NULL;
  }
  // 设置victim的头部,
  set_head(victim, nb | PREV_INUSE |
  (av != &main_arena ? NON_MAIN_ARENA : 0));
  // 设置 remainder 的头部
  set_head(remainder, remainder_size | PREV_INUSE);
  // 设置记录 remainder 大小的 prev_size 字段,因为此时 remainder 处于空闲状态。
  set_foot(remainder, remainder_size);
  // 细致的检查,非调试状态下没有作用
  check_malloced_chunk(av, victim, nb);
  // 将 victim 从 chunk 模式转化为mem模式
  void *p = chunk2mem(victim);
  // 如果设置了perturb_type, 则将获取到的chunk初始化为 perturb_type ^ 0xff
  alloc_perturb(p, bytes);
  return p;
  }

初始取出
            /* remove from unsorted list */
  unsorted_chunks(av)->bk = bck;
  bck->fd                 = unsorted_chunks(av);

EXACT FIT
如果从 unsorted bin 中取出来的 chunk 大小正好合适,就直接使用。这里应该已经把合并后恰好合适的 chunk 给分配出去了。
            /* Take now instead of binning if exact fit */
  if (size == nb) {
  set_inuse_bit_at_offset(victim, size);
  if (av != &main_arena) set_non_main_arena(victim);
  check_malloced_chunk(av, victim, nb);
  void *p = chunk2mem(victim);
  alloc_perturb(p, bytes);
  return p;
  }

PLACE CHUNK IN SMALL BIN
把取出来的 chunk 放到对应的 small bin 中。
            /* place chunk in bin */
  if (in_smallbin_range(size)) {
  victim_index = smallbin_index(size);
  bck          = bin_at(av, victim_index);
  fwd          = bck->fd;

PLACE CHUNK IN LARGE BIN
把取出来的 chunk 放到对应的 large bin 中。
            } else {
  // large bin 范围
  victim_index = largebin_index(size);
  bck          = bin_at(av, victim_index); // 当前 large bin 的头部
  fwd          = bck->fd;
  /* maintain large bins in sorted order */
  /* 从这里我们可以总结出,largebin 以 fd_nextsize 递减排序。
  同样大小的 chunk,后来的只会插入到之前同样大小的 chunk 后,
  而不会修改之前相同大小的fd/bk_nextsize,这也很容易理解,
  可以减低开销。此外,bin 头不参与 nextsize 链接。*/
  // 如果 large bin 链表不空
  if (fwd != bck) {
  /* Or with inuse bit to speed comparisons */
  // 加速比较,应该不仅仅有这个考虑,因为链表里的 chunk 都会设置该位。
  size |= PREV_INUSE;
  /* if smaller than smallest, bypass loop below */
  // bck->bk 存储着相应 large bin 中最小的chunk。
  // 如果遍历的 chunk 比当前最小的还要小,那就只需要插入到链表尾部。
  // 判断 bck->bk 是不是在 main arena。
  assert(chunk_main_arena(bck->bk));
  if ((unsigned long) (size) <
  (unsigned long) chunksize_nomask(bck->bk)) {
  // 令 fwd 指向 large bin 头
  fwd = bck;
  // 令 bck 指向 largin bin 尾部 chunk
  bck = bck->bk;
  // victim 的 fd_nextsize 指向 largin bin 的第一个 chunk
  victim->fd_nextsize = fwd->fd;
  // victim 的 bk_nextsize 指向原来链表的第一个 chunk 指向的 bk_nextsize
  victim->bk_nextsize = fwd->fd->bk_nextsize;
  // 原来链表的第一个 chunk 的 bk_nextsize 指向 victim
  // 原来指向链表第一个 chunk 的 fd_nextsize 指向 victim
  fwd->fd->bk_nextsize =
  victim->bk_nextsize->fd_nextsize = victim;
  } else {
  // 当前要插入的 victim 的大小大于最小的 chunk
  // 判断 fwd 是否在 main arena
  assert(chunk_main_arena(fwd));
  // 从链表头部开始找到不比 victim 大的 chunk
  while ((unsigned long) size < chunksize_nomask(fwd)) {
  fwd = fwd->fd_nextsize;
  assert(chunk_main_arena(fwd));
  }
  // 如果找到了一个和 victim 一样大的 chunk,
  // 那就直接将 chunk 插入到该chunk的后面,并不修改 nextsize 指针。
  if ((unsigned long) size ==
  (unsigned long) chunksize_nomask(fwd))
  /* Always insert in the second position.  */
  fwd = fwd->fd;
  else {
  // 如果找到的chunk和当前victim大小不一样
  // 那么就需要构造 nextsize 双向链表了
  victim->fd_nextsize              = fwd;
  victim->bk_nextsize              = fwd->bk_nextsize;
  fwd->bk_nextsize                 = victim;
  victim->bk_nextsize->fd_nextsize = victim;
  }
  bck = fwd->bk;
  }
  } else
  // 如果空的话,直接简单使得 fd_nextsize 与 bk_nextsize 构成一个双向链表即可。
  victim->fd_nextsize = victim->bk_nextsize = victim;
  }

最终取出
            // 放到对应的 bin 中,构成 bck<-->victim<-->fwd。
  mark_bin(av, victim_index);
  victim->bk = bck;
  victim->fd = fwd;
  fwd->bk    = victim;
  bck->fd    = victim;

WHILE 迭代次数
while 最多迭代 10000 次后退出。
            // #define MAX_ITERS 10000
  if (++iters >= MAX_ITERS) break;
  }

large chunk
如果请求的 chunk 在 large chunk 范围内,就在对应的 bin 中从小到大进行扫描,找到第一个合适的。
        /*
  If a large request, scan through the chunks of current bin in
  sorted order to find smallest that fits.  Use the skip list for this.
  */
  if (!in_smallbin_range(nb)) {
  bin = bin_at(av, idx);
  /* skip scan if empty or largest chunk is too small */
  // 如果对应的 bin 为空或者其中的chunk最大的也很小,那就跳过
  // first(bin)=bin->fd 表示当前链表中最大的chunk
  if ((victim = first(bin)) != bin &&
  (unsigned long) chunksize_nomask(victim) >=
  (unsigned long) (nb)) {
  // 反向遍历链表,直到找到第一个不小于所需chunk大小的chunk
  victim = victim->bk_nextsize;
  while (((unsigned long) (size = chunksize(victim)) <
  (unsigned long) (nb)))
  victim = victim->bk_nextsize;
  /* Avoid removing the first entry for a size so that the skip
  list does not have to be rerouted.  */
  // 如果最终取到的chunk不是该bin中的最后一个chunk,并且该chunk与其前面的chunk
  // 的大小相同,那么我们就取其前面的chunk,这样可以避免调整bk_nextsize,fd_nextsize
  //  链表。因为大小相同的chunk只有一个会被串在nextsize链上。
  if (victim != last(bin) &&
  chunksize_nomask(victim) == chunksize_nomask(victim->fd))
  victim = victim->fd;
  // 计算分配后剩余的大小
  remainder_size = size - nb;
  // 进行unlink
  unlink(av, victim, bck, fwd);
  /* Exhaust */
  // 剩下的大小不足以当做一个块
  // 很好奇接下来会怎么办?
  if (remainder_size < MINSIZE) {
  set_inuse_bit_at_offset(victim, size);
  if (av != &main_arena) set_non_main_arena(victim);
  }
  /* Split */
  //  剩下的大小还可以作为一个chunk,进行分割。
  else {
  // 获取剩下那部分chunk的指针,称为remainder
  remainder = chunk_at_offset(victim, nb);
  /* We cannot assume the unsorted list is empty and therefore
  have to perform a complete insert here.  */
  // 插入unsorted bin中
  bck = unsorted_chunks(av);
  fwd = bck->fd;
  // 判断 unsorted bin 是否被破坏。
  if (__glibc_unlikely(fwd->bk != bck)) {
  errstr = "malloc(): corrupted unsorted chunks";
  goto errout;
  }
  remainder->bk = bck;
  remainder->fd = fwd;
  bck->fd       = remainder;
  fwd->bk       = remainder;
  // 如果不处于small bin范围内,就设置对应的字段
  if (!in_smallbin_range(remainder_size)) {
  remainder->fd_nextsize = NULL;
  remainder->bk_nextsize = NULL;
  }
  // 设置分配的chunk的标记
  set_head(victim,
  nb | PREV_INUSE |
  (av != &main_arena ? NON_MAIN_ARENA : 0));
  // 设置remainder的上一个chunk,即分配出去的chunk的使用状态
  // 其余的不用管,直接从上面继承下来了
  set_head(remainder, remainder_size | PREV_INUSE);
  // 设置remainder的大小
  set_foot(remainder, remainder_size);
  }
  // 检查
  check_malloced_chunk(av, victim, nb);
  // 转换为mem状态
  void *p = chunk2mem(victim);
  // 如果设置了perturb_type, 则将获取到的chunk初始化为 perturb_type ^ 0xff
  alloc_perturb(p, bytes);
  return p;
  }
  }

寻找较大 chunk
如果走到了这里,那说明对于用户所需的 chunk,不能直接从其对应的合适的 bin 中获取 chunk,所以我们需要来查找比当前 bin 更大的 fast bin , small bin 或者 large bin。
        /*
  Search for a chunk by scanning bins, starting with next largest
  bin. This search is strictly by best-fit; i.e., the smallest
  (with ties going to approximately the least recently used) chunk
  that fits is selected.
  The bitmap avoids needing to check that most blocks are nonempty.
  The particular case of skipping all bins during warm-up phases
  when no chunks have been returned yet is faster than it might look.
  */
  ++idx;
  // 获取对应的bin
  bin   = bin_at(av, idx);
  // 获取当前索引在binmap中的block索引
  // #define idx2block(i) ((i) >> BINMAPSHIFT)  ,BINMAPSHIFT=5
  // Binmap按block管理,每个block为一个int,共32个bit,可以表示32个bin中是否有空闲chunk存在
  // 所以这里是右移5
  block = idx2block(idx);
  // 获取当前块大小对应的映射,这里可以得知相应的bin中是否有空闲块
  map   = av->binmap[ block ];
  // #define idx2bit(i) ((1U << ((i) & ((1U << BINMAPSHIFT) - 1))))
  // 将idx对应的比特位设置为1,其它位为0
  bit   = idx2bit(idx);
  for (;;) {

找到一个合适的 MAP
            /* Skip rest of block if there are no more set bits in this block.
  */
  // 如果bit>map,则表示该 map 中没有比当前所需要chunk大的空闲块
  // 如果bit为0,那么说明,上面idx2bit带入的参数为0。
  if (bit > map || bit == 0) {
  do {
  // 寻找下一个block,直到其对应的map不为0。
  // 如果已经不存在的话,那就只能使用top chunk了
  if (++block >= BINMAPSIZE) /* out of bins */
  goto use_top;
  } while ((map = av->binmap[ block ]) == 0);
  // 获取其对应的bin,因为该map中的chunk大小都比所需的chunk大,而且
  // map本身不为0,所以必然存在满足需求的chunk。
  bin = bin_at(av, (block << BINMAPSHIFT));
  bit = 1;
  }

找到合适的 BIN
            /* Advance to bin with set bit. There must be one. */
  // 从当前map的最小的bin一直找,直到找到合适的bin。
  // 这里是一定存在的
  while ((bit & map) == 0) {
  bin = next_bin(bin);
  bit <<= 1;
  assert(bit != 0);
  }

简单检查 CHUNK
            /* Inspect the bin. It is likely to be non-empty */
  // 获取对应的bin
  victim = last(bin);
  /*  If a false alarm (empty bin), clear the bit. */
  // 如果victim=bin,那么我们就将map对应的位清0,然后获取下一个bin
  // 这种情况发生的概率应该很小。
  if (victim == bin) {
  av->binmap[ block ] = map &= ~bit; /* Write through */
  bin                 = next_bin(bin);
  bit <<= 1;
  }

真正取出 CHUNK
            else {
  // 获取对应victim的大小
  size = chunksize(victim);
  /*  We know the first chunk in this bin is big enough to use. */
  assert((unsigned long) (size) >= (unsigned long) (nb));
  // 计算分割后剩余的大小
  remainder_size = size - nb;
  /* unlink */
  unlink(av, victim, bck, fwd);
  /* Exhaust */
  // 如果分割后不够一个chunk怎么办?
  if (remainder_size < MINSIZE) {
  set_inuse_bit_at_offset(victim, size);
  if (av != &main_arena) set_non_main_arena(victim);
  }
  /* Split */
  // 如果够,尽管分割
  else {
  // 计算剩余的chunk的偏移
  remainder = chunk_at_offset(victim, nb);
  /* We cannot assume the unsorted list is empty and therefore
  have to perform a complete insert here.  */
  // 将剩余的chunk插入到unsorted bin中
  bck = unsorted_chunks(av);
  fwd = bck->fd;
  if (__glibc_unlikely(fwd->bk != bck)) {
  errstr = "malloc(): corrupted unsorted chunks 2";
  goto errout;
  }
  remainder->bk = bck;
  remainder->fd = fwd;
  bck->fd       = remainder;
  fwd->bk       = remainder;
  /* advertise as last remainder */
  // 如果在small bin范围内,就将其标记为remainder
  if (in_smallbin_range(nb)) av->last_remainder = remainder;
  if (!in_smallbin_range(remainder_size)) {
  remainder->fd_nextsize = NULL;
  remainder->bk_nextsize = NULL;
  }
  // 设置victim的使用状态
  set_head(victim,
  nb | PREV_INUSE |
  (av != &main_arena ? NON_MAIN_ARENA : 0));
  // 设置remainder的使用状态,这里是为什么呢?
  set_head(remainder, remainder_size | PREV_INUSE);
  // 设置remainder的大小
  set_foot(remainder, remainder_size);
  }
  // 检查
  check_malloced_chunk(av, victim, nb);
  // chunk状态转换到mem状态
  void *p = chunk2mem(victim);
  // 如果设置了perturb_type, 则将获取到的chunk初始化为 perturb_type ^ 0xff
  alloc_perturb(p, bytes);
  return p;
  }

使用 top chunk
如果所有的 bin 中的 chunk 都没有办法直接满足要求(即不合并),或者说都没有空闲的 chunk。那么我们就只能使用 top chunk 了。
    use_top:
  /*
  If large enough, split off the chunk bordering the end of memory
  (held in av->top). Note that this is in accord with the best-fit
  search rule.  In effect, av->top is treated as larger (and thus
  less well fitting) than any other available chunk since it can
  be extended to be as large as necessary (up to system
  limitations).
  We require that av->top always exists (i.e., has size >=
  MINSIZE) after initialization, so if it would otherwise be
  exhausted by current request, it is replenished. (The main
  reason for ensuring it exists is that we may need MINSIZE space
  to put in fenceposts in sysmalloc.)
  */
  // 获取当前的top chunk,并计算其对应的大小
  victim = av->top;
  size   = chunksize(victim);
  // 如果分割之后,top chunk 大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
  if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) {
  remainder_size = size - nb;
  remainder      = chunk_at_offset(victim, nb);
  av->top        = remainder;
  // 这里设置 PREV_INUSE 是因为 top chunk 前面的 chunk 如果不是 fastbin,就必然会和
  // top chunk 合并,所以这里设置了 PREV_INUSE。
  set_head(victim, nb | PREV_INUSE |
  (av != &main_arena ? NON_MAIN_ARENA : 0));
  set_head(remainder, remainder_size | PREV_INUSE);
  check_malloced_chunk(av, victim, nb);
  void *p = chunk2mem(victim);
  alloc_perturb(p, bytes);
  return p;
  }
  // 否则,判断是否有 fast chunk
  /* When we are using atomic ops to free fast chunks we can get
  here for all block sizes.  */
  else if (have_fastchunks(av)) {
  // 先执行一次fast bin的合并
  malloc_consolidate(av);
  /* restore original bin index */
  // 判断需要的chunk是在small bin范围内还是large bin范围内
  // 并计算对应的索引
  // 等待下次再看看是否可以
  if (in_smallbin_range(nb))
  idx = smallbin_index(nb);
  else
  idx = largebin_index(nb);
  }

堆内存不够
如果堆内存不够,我们就需要使用 sysmalloc 来申请内存了。
        /*
  Otherwise, relay to handle system-dependent cases
  */
  // 否则的话,我们就只能从系统中再次申请一点内存了。
  else {
  void *p = sysmalloc(nb, av);
  if (p != NULL) alloc_perturb(p, bytes);
  return p;
  }

_libc_calloc
calloc 也是 libc 中的一种申请内存块的函数。在 libc中的封装为 _libc_calloc,具体介绍如下
/*
  calloc(size_t n_elements, size_t element_size);
  Returns a pointer to n_elements * element_size bytes, with all locations
  set to zero.
  */
  void*  __libc_calloc(size_t, size_t);

sysmalloc
正如该函数头的注释所言,该函数用于当前堆内存不足时,需要向系统申请更多的内存。
基本定义
static void *sysmalloc(INTERNAL_SIZE_T nb, mstate av) {
  mchunkptr old_top;        /* incoming value of av->top */
  INTERNAL_SIZE_T old_size; /* its size */
  char *old_end;            /* its end address */
  long size; /* arg to first MORECORE or mmap call */
  char *brk; /* return value from MORECORE */
  long correction; /* arg to 2nd MORECORE call */
  char *snd_brk;   /* 2nd return val */
  INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of new space */
  INTERNAL_SIZE_T end_misalign;   /* partial page left at end of new space */
  char *aligned_brk;              /* aligned offset into brk */
  mchunkptr p;                  /* the allocated/returned chunk */
  mchunkptr remainder;          /* remainder frOm allocation */
  unsigned long remainder_size; /* its size */
  size_t pagesize = GLRO(dl_pagesize);
  bool tried_mmap = false;

pagesize=4096=0x100
考虑 mmap
看不下去了orz,先放放


温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046

小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

GMT+8, 2024-4-20 14:00 , Processed in 0.113394 second(s), 42 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表