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服務請求

Ø last remainder chunk服務請求

(用戶請求爲 small chunk

last remainder chunkunsorted bin 中的唯一的chunk

Last remainder大小大於用戶請求的大小;)

Ø 嚴格匹配(exact fit)服務請求

Ø 如果是small chunk,則插入到small bin鏈表的開頭

Ø 如果爲large chunk,則按大小降序放入large bin(大小相同時總是插入第二個位置);同時構造fd_nextsize/bk_nextsize鏈表;

l Large bin服務large chunk請求

如果找到則切分後將剩餘的部分放入unsorted bin

l next largest bin服務請求

在下一個非空binbin數組中下一個索引)中獲取chunk,切分後返還給用戶

l top chunk服務請求

合併fast bin,下次嘗試

l 從系統請求內存

2.5.1 convert

將用戶申請的內存大小轉換爲內部的chunk大小

checked_request2size (bytes, nb);

2.5.2 arena

如果沒有可用的arena,則使用sysmallocmmap獲取chunk

/* 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;

}

2.5.3 Fastbin

如果申請的 chunk 的大小位於 fast bin 範圍內,則從對應的fast bin鏈表的頭部中獲取並返回free chunk(fast bin是單鏈表)

 

/*

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 ()))

{

idx = fastbin_index (nb);

mfastbinptr *fb = &fastbin (av, idx);

mchunkptr pp = *fb;

// 獲取fast bin鏈表中的第一個元素,同時更新fastbinsY中的fast bin頭指針

// 如果fast bin鏈表是空的,則退出循環

do

{

victim = pp;

if (victim == NULL)

break;

}

while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim))

!= victim);

if (victim != 0)

{

     // 檢查取到的 chunk 大小是否與相應的 fastbin 索引一致。

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);

void *p = chunk2mem (victim);

// 如果設置了perturb_type, 則將獲取到的chunk初始化爲 perturb_type ^ 0xff

alloc_perturb (p, bytes);

return p;

}

}

2.5.4 Small bin

如果申請的 chunk 的大小位於 small bin 範圍內(small bin是環形循環雙鏈表),則從鏈表中取出small bin的最後一個chunk返回給用戶;

/*

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))

{

idx = smallbin_index (nb);

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 */

malloc_consolidate (av);

// 第二種情況,small bin 中存在空閒的 chunk

else

{

// 獲取 small bin 中倒數第二個 chunk

bck = victim->bk;

// 獲取 small bin 中倒數第二個 chunk

            if (__glibc_unlikely (bck->fd != victim))

{

errstr = "malloc(): smallbin double linked list corrupted";

goto errout;

}

set_inuse_bit_at_offset (victim, nb);

// 修改 small bin 鏈表,將 small bin 的最後一個 chunk 取出來

bin->bk = bck;

bck->fd = bin;

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;

}

}

}

2.5.5 malloc_consolidate

如果是large chunk,繼續之前,先合併 fastbins。這可以解決一般由fastbins帶來的分片問題。實際上,程序要麼請求small chunk,要麼請求large 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

{

idx = largebin_index (nb);

if (have_fastchunks (av))

malloc_consolidate (av);

}

2.5.6 大循環

l unsorted bin服務請求

Ø last remainder chunk服務請求

(用戶請求爲 small chunk

last remainder chunkunsorted bin 中的唯一的chunk

Last remainder大小大於用戶請求的大小;)

Ø 嚴格匹配(exact fit)服務請求

Ø 如果是small chunk,則插入到small bin鏈表的開頭

Ø 如果爲large chunk,則按大小降序放入large bin(大小相同時總是插入第二個位置);同時構造fd_nextsize/bk_nextsize鏈表;

l Large bin服務large chunk請求

如果找到則切分後將剩餘的部分放入unsorted bin

l next largest bin服務請求

在下一個非空binbin數組中下一個索引)中獲取chunk,切分後返還給用戶

l top chunk服務請求

l fast bin服務請求

l 從系統請求內存

 

1. 這裏使用外循環,是因爲我們可能在處理中合併了一個滿足要求的chunk,需要重新嘗試。最多重新嘗試一次,否則我們將擴展內存來服務請求。

 

for (;; )

{

int iters = 0;

//......

}

2.5.7 大循環-unsorted bin遍歷

使用bk遍歷unsorted bin

2.5.7.1 獲取一個unsorted chunk

// 如果 unsorted bin 不爲空

while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))

{

// victim 爲 unsorted bin 的最後一個 chunk

// bck 爲 unsorted bin 的倒數第二個 chunk

bck = victim->bk;

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);

2.5.7.2 last remainder chunk中獲取

如果滿足以下條件,則使用last remainder chunk服務請求。這會有助於使得連續的small chunk的請求得到的位置相鄰。

1) 用戶請求爲 small chunk

2) last remainder chunkunsorted bin 中的唯一的chunk

3) Last remainder大小大於用戶請求的大小

 

/*

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.

*/

 

//滿足3個條件

if (in_smallbin_range (nb) &&

bck == unsorted_chunks (av) &&

victim == av->last_remainder &&

(unsigned long) (size) > (unsigned long) (nb + MINSIZE))

{

   //從last remainder chunk中取出用戶請求的chunk,更新unsorted bin鏈表和標誌

/* split and reattach remainder */

remainder_size = size - nb;

remainder = chunk_at_offset (victim, nb);

unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;

    av->last_remainder = remainder;

    remainder->bk = remainder->fd = unsorted_chunks (av);

    if (!in_smallbin_range (remainder_size))

    {

    remainder->fd_nextsize = NULL;

    remainder->bk_nextsize = NULL;

    }

    set_head (victim, nb | PREV_INUSE |

    (av != &main_arena ? NON_MAIN_ARENA : 0));

    set_head (remainder, remainder_size | PREV_INUSE);

    set_foot (remainder, remainder_size);

    check_malloced_chunk (av, victim, nb);

    void *p = chunk2mem (victim);

    alloc_perturb (p, bytes);

    return p;

}

2.5.7.3 Unsorted chunk從鏈表中移除

/* remove from unsorted list */

unsorted_chunks (av)->bk = bck;

bck->fd = unsorted_chunks (av);

 

移除後要麼被使用,要麼返回給用戶(exact fit),要麼放入相關的bin中;

2.5.7.4 精確匹配

如果取出的unsorted 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;

}

2.5.7.5 unsorted chunk放入small bin

如果是small chunk,則插入到small bin鏈表的開頭

if (in_smallbin_range (size))

{

  victim_index = smallbin_index (size);

  bck = bin_at (av, victim_index);

  fwd = bck->fd;

}

else ......

 

// 放到對應的 bin 中,插入victim,構成 bck<-->victim<-->fwd

mark_bin (av, victim_index);

victim->bk = bck;

victim->fd = fwd;

fwd->bk = victim;

bck->fd = victim;

 

#define MAX_ITERS 10000

if (++iters >= MAX_ITERS)

break;

2.5.7.6 unsorted chunk放入large bin

如果爲large chunk,則按大小降序放入large bin(大小相同時總是插入第二個位置);同時構造fd_nextsize/bk_nextsize鏈表;

else

{

  // large bin範圍,bck爲頭結點,fwd爲第一個結點

  victim_index = largebin_index (size);

  bck = bin_at (av, victim_index);

  fwd = bck->fd;

 

  /* maintain large bins in sorted order */

  // bin爲非空鏈表

  if (fwd != bck)

  {

/* Or with inuse bit to speed comparisons */

// 加速比較,鏈表裏的chunk都會設置該位

    size |= PREV_INUSE;

/* if smaller than smallest, bypass loop below */

//爲什麼最後一個chunk屬於main arena呢?

assert (chunk_main_arena (bck->bk));

//遍歷的chunk比當前最小的還要小

    if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))

{

  //bck爲最後一個結點,fw爲頭結點

      fwd = bck;

      bck = bck->bk;

      //victim插入fd_nextsize/bk_nextsize鏈表

      victim->fd_nextsize = fwd->fd;

      victim->bk_nextsize = fwd->fd->bk_nextsize;

      fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;

    }

    else

{//遍歷的chunk比當前最小的要大於等於

  //爲什麼第一個chunk屬於main arena呢?

      assert (chunk_main_arena (fwd));

// 從鏈表頭部開始找到大小小於等於 victim 的 chunk

      while ((unsigned long) size < chunksize_nomask (fwd))

      {

        fwd = fwd->fd_nextsize;

        //爲什麼每個chunk都屬於main arena呢?

       assert (chunk_main_arena (fwd));

      }

// 如果大小一樣插入第二個位置,nextsize指針沒有改變,不需要修改

      if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))

      /* Always insert in the second position. */

        fwd = fwd->fd;

      else

      {// 如果大小不一樣,則插入fd_nextsize/bk_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//空鏈表

    victim->fd_nextsize = victim->bk_nextsize = victim;

}

mark_bin (av, victim_index);

victim->bk = bck;

victim->fd = fwd;

fwd->bk = victim;

bck->fd = victim;

2.5.8 大循環-large bin服務large chunk請求

如果請求的是large chunk,就在large  bin 中從小到大進行掃描,找到第一個合適的。如果找到則切分後將剩餘的部分放入unsorted 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);

  // 如果對應的 bin 爲空或者其中最大的chunk也小於用戶的請求,那就跳過

  /* skip scan if empty or largest chunk is too small */

  if ((victim = first (bin)) != bin && (unsigned long) chunksize_nomask (victim) >= (unsigned long) (nb))

  {

victim = victim->bk_nextsize;

// 反向遍歷鏈表,直到找到第一個不小於用戶請求大小的chunk

    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與其前面的chunkfd)

 // 的大小相同,那麼我們就取其前面的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 (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 */

    else  //  剩下的大小還可以作爲一個chunk,進行分割,剩餘部分放入unsorted bin

    {

      remainder = chunk_at_offset (victim, nb);

      /* We cannot assume the unsorted list is empty and therefore

      have to perform a complete insert here. */

      bck = unsorted_chunks (av);

      fwd = bck->fd;

      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;

      if (!in_smallbin_range (remainder_size))

      {

        remainder->fd_nextsize = NULL;

        remainder->bk_nextsize = NULL;

      }

      set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));

      set_head (remainder, remainder_size | PREV_INUSE);

      set_foot (remainder, remainder_size);

    }

    check_malloced_chunk (av, victim, nb);

    void *p = chunk2mem (victim);

    alloc_perturb (p, bytes);

    return p;

  }

}

2.5.9 大循環-尋找next largest bin

如果走到了這裏,那說明對於用戶所需的chunk,不能直接從其對應的bin中獲取chunk,此時我們在下一個非空binbin數組中下一個索引)中獲取chunk,切分後返還給用戶。

 

【準備工作】

// 從下一個更大的bin開始掃描最小匹配的chunk。

// 利用bitmap可以避免掃描空的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.

*/

// 獲取下一個更大的bin

++idx;

bin = bin_at (av, idx);

 

//獲取對應的bitmap標記

block = idx2block (idx);

map = av->binmap[block];

bit = idx2bit (idx);

for (;; )

{

//......

}

 

找到合適的map--跳過位沒有設置的block

/* Skip rest of block if there are no more set bits in this block. */

//bit > map: bit中只有一個1,這說明map中這個1對應的高位均爲0,

//爲0表示對應的bin爲空,則此block可以跳過

//bit <= map && bit == 0: bit = idx2bit (idx); 

//bit初始化時不可能爲0(有一個位置1),但是後面有左移位操作,如果剛好移到了最高位,則會爲0

//bit爲0時會進入下一個block,bit重新初始化爲1

if (bit > map || bit == 0)

{

  do

  {

//跳過block

    if (++block >= BINMAPSIZE) /* out of bins */

      goto use_top;

  }

  while ((map = av->binmap[block]) == 0);//map爲0表示此block所有的位均爲0,跳過

 

  bin = bin_at (av, (block << BINMAPSHIFT));

  bit = 1;

}

 

找到合適的bin】

//找到一個非空的bin

/* Advance to bin with set bit. There must be one. */

while ((bit & map) == 0)

{

bin = next_bin (bin);

bit <<= 1;

assert (bit != 0);

}

 

檢查bin是否爲空

爲空時清空binMap中對應的標誌位

/* Inspect the bin. It is likely to be non-empty */

victim = last (bin);

 

/* If a false alarm (empty bin), clear the bit. */

if (victim == bin)

{

av->binmap[block] = map &= ~bit; /* Write through */

bin = next_bin (bin);

bit <<= 1;

}

 

【unlink-切分-獲取chunk

這部分的代碼和一般的切分代碼流程差不多,在“大循環-large bin服務large chunk請求”中也可以看到;

 

else

{

  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 */

  // 剩下的大小不足以當做一個塊,設置標誌,不切分

  if (remainder_size < MINSIZE)

  {

    set_inuse_bit_at_offset (victim, size);

    if (av != &main_arena)

     set_non_main_arena (victim);

  }

  /* Split */

  else  //  剩下的大小還可以作爲一個chunk,進行分割,剩餘部分放入unsorted bin

  {

    remainder = chunk_at_offset (victim, nb);

    /* We cannot assume the unsorted list is empty and therefore

    have to perform a complete insert here. */

    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 */

    if (in_smallbin_range (nb))

      av->last_remainder = remainder;

    if (!in_smallbin_range (remainder_size))

    {

      remainder->fd_nextsize = NULL;

      remainder->bk_nextsize = NULL;

    }

    set_head (victim, nb | PREV_INUSE | av != &main_arena ? NON_MAIN_ARENA : 0));

    set_head (remainder, remainder_size | PREV_INUSE);

    set_foot (remainder, remainder_size);

  }

  check_malloced_chunk (av, victim, nb);

  void *p = chunk2mem (victim);

  alloc_perturb (p, bytes);

  return p;

}

2.5.10 大循環-使用top chunk

如果所有的 bin 中的 chunk 都沒有辦法直接滿足要求(即不合並),或者說都沒有空閒的 chunk。那麼我們就只能使用 top chunk 了。

 

l Top chunk服務請求

合併fast bin,下次嘗試

l 從系統請求內存

 

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.)

*/

  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;

    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;

}

/* When we are using atomic ops to free fast chunks we can get

here for all block sizes. */

// 否則,判斷是否有 fast chunk

  else if (have_fastchunks (av))

  {

// 先執行一次fast bin的合併

malloc_consolidate (av);

// 判斷需要的chunk是在small bin範圍內還是large bin範圍內

// 並計算對應的索引

// 等待下次再看看是否可以

    /* restore original bin index */

    if (in_smallbin_range (nb))

      idx = smallbin_index (nb);

    else

      idx = largebin_index (nb);

  }

/*

Otherwise, relay to handle system-dependent cases

*/

  // 否則的話,我們就只能從系統中再次申請一點內存了

  else

  {

    void *p = sysmalloc (nb, av);

    if (p != NULL)

      alloc_perturb (p, bytes);

    return p;

  }

2.6 Free

見註釋,最終調用的是_int_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.

*/

// 除非使用mallopt關閉,釋放非常大的空間會自動觸發將不使用的內存歸還給系統

void __libc_free(void*);

libc_hidden_proto (__libc_free)

strong_alias (__libc_free, __cfree) weak_alias (__libc_free, cfree)

strong_alias (__libc_free, __free) strong_alias (__libc_free, free)

void

__libc_free (void *mem)

{

  mstate ar_ptr;

  mchunkptr p; /* chunk corresponding to mem */

 

  void (*hook) (void *, const void *) = atomic_forced_read (__free_hook);

  // 判斷是否有鉤子函數 __free_hook

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

  {

    (*hook)(mem, RETURN_ADDRESS (0));

    return;

  }

  if (mem == 0) /* free(0) has no effect */

    return;

  p = mem2chunk (mem);

  // 如果該塊內存是mmap得到的

  if (chunk_is_mmapped (p)) /* release mmapped memory. */

  {

    /* See if the dynamic brk/mmap threshold needs adjusting.

     Dumped fake mmapped chunks do not affect the threshold. */

    if (!mp_.no_dyn_threshold

      && chunksize_nomask (p) > mp_.mmap_threshold

      && chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX

       && !DUMPED_MAIN_ARENA_CHUNK (p))

    {

      mp_.mmap_threshold = chunksize (p);

      mp_.trim_threshold = 2 * mp_.mmap_threshold;

      LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2,

      mp_.mmap_threshold, mp_.trim_threshold);

    }

    munmap_chunk (p);

    return;

  }

  ar_ptr = arena_for_chunk (p);

  _int_free (ar_ptr, p, 0);

}

2.7 _int_free

static void

_int_free (mstate av, mchunkptr p, int have_lock)

 

功能:釋放chunk,放入fast bin或合併後放入unsorted bin或合併到top chunk

具體過程如下:

如果屬於fast bin,則插入fast bin鏈表頭部;

否則如果不是mmap的內存,則進行合併;

後向合併-合併低地址 CHUNK

下一塊不是top chunk-前向合併-合併高地址chunk

合併後把 chunk 放在 unsorted chunk 鏈表的頭部

下一塊是top chunk-合併到top chunk

如果合併後chunk大小大於64KB,則合併fast chunk,收縮top;

否則如果是mmap的內存,則釋放mmapchunk

2.7.1 簡單的檢查

檢查大小與對齊

size = chunksize (p);

 

/* Little security check which won't hurt performance: the

allocator never wrapps around at the end of the address space.

Therefore we can exclude some size values which might appear

here by accident or by "design" from some intruder. */

//如果p+size繞過了地址空間的結尾,或者p沒有對齊,則出錯了

if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)

|| __builtin_expect (misaligned_chunk (p), 0))

{

  errstr = "free(): invalid pointer";

errout:

  if (!have_lock && locked)

    __libc_lock_unlock (av->mutex);

  malloc_printerr (check_action, errstr, chunk2mem (p), av);

  return;

}

/* We know that each chunk is at least MINSIZE bytes in size or a

multiple of MALLOC_ALIGNMENT. */

// 大小沒有最小的chunk大,或者說,大小不是MALLOC_ALIGNMENT的整數倍,則有問題

if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))

{

  errstr = "free(): invalid size";

  goto errout;

}

// 檢查該chunk是否處於使用狀態,非調試狀態下沒有作用

check_inuse_chunk(av, p);

2.7.2 Fast bin

如果屬於fast bin,則插入fast bin鏈表頭部;

/*

If eligible, place chunk on a fastbin so it can be found

and used quickly in malloc.

*/

//大小屬於fast bin

if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())

#if TRIM_FASTBINS

/*

  If TRIM_FASTBINS set, don't place chunks

  bordering top into fastbins

*/

//默認 #define TRIM_FASTBINS 0,因此默認情況下下面的語句不會執行

//如果當前chunk是fast chunk,並且下一個chunk是top chunk,則不能插入

//此時由於定義了TRIM_FASTBINS,這個chunk會和top chunk合併

&& (chunk_at_offset(p, size) != av->top)

#endif

)

{

  //檢查下一個chunk的大小上限和下限

  // 下一個chunk的大小不能小於兩倍的SIZE_SZ,並且

  // 下一個chunk的大小不能大於system_mem, 一般爲132k

  // 如果出現這樣的情況,就報錯

  if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))

       <= 2 * SIZE_SZ, 0)

  || __builtin_expect (chunksize (chunk_at_offset (p, size))

       >= av->system_mem, 0))

  {

    /* We might not have a lock at this point and concurrent modifications

     of system_mem might have let to a false positive. Redo the test

     after getting the lock. */

    if (have_lock

     || ({ assert (locked == 0);

     __libc_lock_lock (av->mutex);

     locked = 1;

     chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ

     || chunksize (chunk_at_offset (p, size)) >= av->system_mem;

     }))

     {

       errstr = "free(): invalid next size (fast)";

         goto errout;

     }

    if (! have_lock)

     {

       __libc_lock_unlock (av->mutex);

       locked = 0;

     }

  }

  // 將chunk的mem部分全部設置爲perturb_byte

  free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);

  // 設置fast chunk的標記位

  set_fastchunks(av);

  unsigned int idx = fastbin_index(size);

  fb = &fastbin (av, idx);

  /* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */

  //將P插入fast bin鏈表頭部

  mchunkptr old = *fb, old2;

  unsigned int old_idx = ~0u;

  do

  {

  /* Check that the top of the bin is not the record we are going to add

   (i.e., double free). */

// 防止對 fast bin double free

    if (__builtin_expect (old == p, 0))

     {

       errstr = "double free or corruption (fasttop)";

       goto errout;

     }

    /* Check that size of fastbin chunk at the top is the same as

     size of the chunk that we are adding. We can dereference OLD

     only if we have the lock, otherwise it might have already been

     deallocated. See use of OLD_IDX below for the actual check. */

    if (have_lock && old != NULL)

     old_idx = fastbin_index(chunksize(old));

    //P插入fast bin鏈表頭部

    p->fd = old2 = old;

  }

  while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);

  if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))

  {

    errstr = "invalid fastbin entry (free)";

    goto errout;

  }

}

2.7.3 合併非 mmap 的空閒 chunk

合併其他非mmapchunks

 

/*

Consolidate other non-mmapped chunks as they arrive.

*/

 

else if (!chunk_is_mmapped(p)) {

if (! have_lock) {

__libc_lock_lock (av->mutex);

locked = 1;

}

nextchunk = chunk_at_offset(p, size);

 

檢查是否有異常情況

/* Lightweight tests: check whether the block is already the

top block. */

// 當前free的chunk不能是top chunk

if (__glibc_unlikely (p == av->top))

{

   errstr = "double free or corruption (top)";

   goto errout;

}

/* Or whether the next chunk is beyond the boundaries of the arena. */

// 當前free的chunk的下一個chunk不能超過arena的邊界

if (__builtin_expect (contiguous (av)

       && (char *) nextchunk

       >= ((char *) av->top + chunksize(av->top)), 0))

{

   errstr = "double free or corruption (out)";

   goto errout;

}

/* Or whether the block is actually not marked used. */

// 當前要free的chunk的使用標記沒有被標記,double free

if (__glibc_unlikely (!prev_inuse(nextchunk)))

{

   errstr = "double free or corruption (!prev)";

   goto errout;

}

 

//下一個chunk的大小是否超出範圍

nextsize = chunksize(nextchunk);

if (__builtin_expect (chunksize_nomask (nextchunk) <= 2 * SIZE_SZ, 0)

   || __builtin_expect (nextsize >= av->system_mem, 0))

{

   errstr = "free(): invalid next size (normal)";

   goto errout;

}

填充

free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);

 

後向合併-合併低地址 CHUNK

/* consolidate backward */

//如果低地址chunk空閒,則合併後unlink

if (!prev_inuse(p)) {

  prevsize = prev_size (p);

  size += prevsize;

  p = chunk_at_offset(p, -((long) prevsize));

  unlink(av, p, bck, fwd);

}

 

下一塊不是top chunk-前向合併-合併高地址chunk

合併後把 chunk 放在 unsorted chunk 鏈表的頭部

if (nextchunk != av->top) {

  /* get and clear inuse bit */

  nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

 

  /* consolidate forward */

  // 如果不在使用,合併,否則清空當前chunk的使用狀態

  if (!nextinuse) {

    unlink(av, nextchunk, bck, fwd);

    size += nextsize;

  } else

     clear_inuse_bit_at_offset(nextchunk, 0);

  /*

  Place the chunk in unsorted chunk list. Chunks are

  not placed into regular bins until after they have

  been given one chance to be used in malloc.

  */

  // 把 chunk 放在 unsorted chunk 鏈表的頭部

  bck = unsorted_chunks(av);

  fwd = bck->fd;

  if (__glibc_unlikely (fwd->bk != bck))

  {

    errstr = "free(): corrupted unsorted chunks";

    goto errout;

  }

  p->fd = fwd;

  p->bk = bck;

  if (!in_smallbin_range(size))

  {

    p->fd_nextsize = NULL;

    p->bk_nextsize = NULL;

  }

  bck->fd = p;

  fwd->bk = p;

  set_head(p, size | PREV_INUSE);

  set_foot(p, size);

  check_free_chunk(av, p);

}

 

下一塊是top chunk-合併到top chunk

/*

If the chunk borders the current high end of memory,

consolidate into top

*/

 

else {

  size += nextsize;

  set_head(p, size | PREV_INUSE);

  av->top = p;

  check_chunk(av, p);

}

 

向系統返還內存

如果合併後chunk大小大於64KB,則合併fast chunk,收縮top;

/*

If freeing a large space, consolidate possibly-surrounding

chunks. Then, if the total unused topmost memory exceeds trim

threshold, ask malloc_trim to reduce top.

 

Unless max_fast is 0, we don't know if there are fastbins

bordering top, so we cannot tell for sure whether threshold

has been reached unless fastbins are consolidated. But we

don't want to consolidate on each free. As a compromise,

consolidation is performed if FASTBIN_CONSOLIDATION_THRESHOLD

is reached.

*/

//如果釋放大的空間,會合並周邊的chunks。

//然後,如果top超過了trim的閾值,則使用malloc_trim來減少top

//在此之前,我們要先合併fast bin

//我們並不想每一次free都執行合併,作爲一種折中,

//如果到達FASTBIN_CONSOLIDATION_THRESHOLD64KB)才合併fast bin

//合併才執行top的收縮

if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {

  if (have_fastchunks(av))

   malloc_consolidate(av);

  if (av == &main_arena) {

    #ifndef MORECORE_CANNOT_TRIM

    if ((unsigned long)(chunksize(av->top)) >=(unsigned long)(mp_.trim_threshold))

      systrim(mp_.top_pad, av);

    #endif

  } else {

    /* Always try heap_trim(), even if the top chunk is not

    large, because the corresponding heap might go away. */

    heap_info *heap = heap_for_ptr(top(av));

    assert(heap->ar_ptr == av);

    heap_trim(heap, mp_.top_pad);

  }

}

if (! have_lock) {

  assert (locked);

  __libc_lock_unlock (av->mutex);

}

2.7.4 釋放mmapchunk

/*

If the chunk was allocated via mmap, release via munmap().

*/

 

else {

munmap_chunk (p);

}

1.8 malloc_consolidate

功能:清除fast chunk,能合併則合併,放入unsorted bin或併入top chunk

 

具體過程如下:

如果fast bin已經初始化

遍歷fast bin

遍歷fast chunk

如果低地址chunk空閒,合併之,unlink

如果高地址不是top chunk

如果chunk空閒,合併之,unlink

Fast chunk放入unsorted bin的頭部

如果高地址是top chunk

合併到 top

如果fast bin沒有初始化

調用malloc_init_state初始化malloc_state

 

/*

------------------------- 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.

 

Also, because this routine needs to be called the first time through

malloc anyway, it turns out to be the perfect place to trigger

initialization code.

*/

//合併fast bin或初始化malloc_state

static void malloc_consolidate(mstate av)

 

合併fast chunk

// fast bin已經初始化

if (get_max_fast () != 0) {

  // 清除fastbin 標記

  clear_fastchunks(av);

 

  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.

  */

  //移除併合並fast bin中的每個chunk,將其放入unsorted bin

  maxfb = &fastbin (av, NFASTBINS - 1); //最大的索引:9,實際上遍歷的是fast bin 0~8

  fb = &fastbin (av, 0);

  do {

    p = atomic_exchange_acq (fb, NULL);

    if (p != 0) {

      do {//遍歷某個fast bin中的fast chunk

        check_inuse_chunk(av, p);

        nextp = p->fd;

        /* Slightly streamlined version of consolidation code in free() */

        //free()中的合併代碼的簡單流線型版本

        size = chunksize (p);

        nextchunk = chunk_at_offset(p, size);

        nextsize = chunksize(nextchunk);

        //如果低地址chunk空閒,則合併後unlink,後面會放入unsorted chunk

        if (!prev_inuse(p)) {

          prevsize = prev_size (p);

          size += prevsize;

          p = chunk_at_offset(p, -((long) prevsize));

          unlink(av, p, bck, fwd);

        }

//下一塊不是top chunk-前向合併-合併高地址chunk

//合併後把 chunk 放在 unsorted chunk 鏈表的頭部

        if (nextchunk != av->top) {

          nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

          if (!nextinuse) {

            size += nextsize;

            unlink(av, nextchunk, bck, fwd);

          } else

            clear_inuse_bit_at_offset(nextchunk, 0);

          first_unsorted = unsorted_bin->fd;

          unsorted_bin->fd = p;

          first_unsorted->bk = p;

          if (!in_smallbin_range (size)) {

            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-合併到top chunk

          size += nextsize;

          set_head(p, size | PREV_INUSE);

          av->top = p;

        }

      } while ( (p = nextp) != 0);

}

  } while (fb++ != maxfb);

}

 

初始化malloc_state

else {

  malloc_init_state(av);

  check_malloc_state(av);

}

結論

【主要的API

1. malloc_consolidate

功能:

清空fast binsfast chunk能合併則合併,放入unsorted bin或併入top chunk

 

具體過程如下:

如果fast bin已經初始化

遍歷fast bin

遍歷fast chunk

如果低地址chunk空閒,合併之,unlink

如果高地址不是top chunk

如果chunk空閒,合併之,unlink

Fast chunk放入unsorted bin的頭部

如果高地址是top chunk

合併到 top

如果fast bin沒有初始化

調用malloc_init_state初始化malloc_state

 

2. 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鏈表中摘除

 

3. _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) 從系統請求內存

4. _int_free

功能:釋放chunk,放入fast bin或合併後放入unsorted bin或合併到top chunk

具體過程如下:

如果屬於fast bin,則插入fast bin鏈表頭部;

否則如果不是mmap的內存,則進行合併;

後向合併-合併低地址 CHUNK

下一塊不是top chunk-前向合併-合併高地址chunk

合併後把 chunk 放在 unsorted chunk 鏈表的頭部

下一塊是top chunk-合併到top chunk

如果合併後chunk大小大於64KB,則合併fast chunk,收縮top;

否則如果是mmap的內存,則釋放mmapchunk

參考文檔

1. https://ctf-wiki.github.io/ctf-wiki/pwn/heap/heap_structure/

2. https://ctf-wiki.github.io/ctf-wiki/pwn/heap/heap_implementation_details/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章