write by say2

glibc malloc.c에 있는 _int_malloc의 소스를 몇가지 주석을 없애거나 번역하고(번역표시 - ***) 약간의 exploit 관련된 설명이나 코드설명을 달았습니다.

그리고 malloc함수를 분석한 다른 글들을 가져와 관련된 코드에 덧붙여 놓았습니다. (갈색 글씨)

**chunk 나 bin에 대한 malloc에 대한 기본적인 지식을 갖추었다고 생각하고 작성하였습니다.

**마지막에 small bin처리와 top chunk처리에 대해서 좀더 분석하여 추가할 예정입니다.


libc_malloc을 호출하면 몇가지 루틴을 거친후 _int_malloc을 호출합니다.

우리가 아는 기능을 하는게 _int_malloc이 됩니다


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;


  checked_request2size (bytes, nb);


사용가능한 공간이 더이상 없을 경우 sysmalloc을 호출합니다. sysmalloc은 시스템에 메모리를 요구할 수 있는 함수 입니다

sysmalloc에서는 mmap을 호출하는데 이를 통해서 top chunk의 크기를 확장하거나 대체하여 동적 할당 영역을 넓힐 수 있습니다.

참고로 sysmalloc은 기존의 old 영역을 free시키고 새로 mmap하는데 여기서 나오는 free를 이용해서 exploit을 짜는 ctf문제도 있었습니다.


  if (__glibc_unlikely (av == NULL))

    {

      void *p = sysmalloc (nb, av);

      if (p != NULL)

alloc_perturb (p, bytes);

      return p;

    }


  /*

     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.

   */


제일먼저 요구한 size가 fastbin영역인지 체크합니다. 이는 M_MXFAST라는 변수로 fastbin의 max size가 결정되는데 default는 64byte입니다.

M_MXFAST는 환경 변수로 조작가능하며 정식으로는 mallopt() 함수에 의해서 setting됩니다. 또한 좀더 high level에서는 초반 get_max_fast()에 의해서 M_MXFAST에 따라 정의되는 global_max_fast라는 변수가 있는데 이 변수로 fastbin의 boundary를 조작할 수 있습니다.


  1. 아래 코드 루틴 )
  2. 주어진 크기가 fast bin에 속한다면 (<= 72) fast bin 내의 free chunk를 찾아본다.
    1.  주어진 크기에 맞는 fast bin의 인덱스를 계산한다.
    2.  해당 인덱스의 포인터가 가리키는 chunk를 victim 지역 변수에 저장한다.
    3.  victim이 NULL이 아니라면 fast bin의 해당 인덱스에 victim->fb가 가리키는 chunk를 저장하고 victim의 데이터 영역에 대한 포인터를 반환한다. (종료)

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

    {

      idx = fastbin_index (nb);

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

      mchunkptr pp = *fb;

      do

        {

          victim = pp;

          if (victim == NULL)

            break;

        }

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

             != victim);

      if (victim != 0)

        {

          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;

            }

          check_remalloced_chunk (av, victim, nb);

          void *p = chunk2mem (victim);

          alloc_perturb (p, bytes);

          return p;

        }

    }



***

그다음은 smallbin영역인지 체크합니다.

smallbin은 각각 하나의 사이즈를 유지하기 때문에 bin들 안에서 검색을 하는건 무의미 합니다.

large요청이라면 우리는 unsorted chunk에서 가장 적합한 놈을 찾을 때까지 기다려야 합니다.

그러나 small은 어떤것에도 잘 맞아서 할당 해제가 large보다 빠르죠

***

  1. 주어진 크기가 small bin에 속한다면 (< 512) small bin 내에서 free chunk를 찾아본다.
    1.  주어진 크기에 맞는 small bin의 인덱스를 계산하여 idx 지역 변수에 저장한다.
    2.  해당 인덱스 내에 가장 오래된 chunk를 victim 지역 변수에 저장한다.(LIFO-last in first out)
    3.  victim이 올바른 chunk를 가리킨다면 해당 인덱스 내의 리스트에서 victim을 제거하고, victim 바로 다음에 위치한 chunk의 헤더에 P (PREV_INUSE) 플래그를 설정한 뒤 victim의 데이터 영역에 대한 포인터를 반환한다. (종료) 설명을 단순하게 하기 위해 앞으로 'victim을 반환한다'라는 표현은 P 플래그를 설정하는 것과 데이터 포인터를 반환하는 작업을 뜻하는 것으로 사용할 것이다.
    4.  victim이 올바른 chunk를 가리키지 않는다는 것은 다음의 두 경우 중 하나이다.
    5.   victim이 NULL이면 최초로 malloc() 함수가 호출된 경우이다. 아직 초기화가 제대로 이루어지지 않았으므로 malloc_init_state() 내부 함수를 호출하여 초기화를 수행한다.
    6.   victim이 NULL이 아니고 bin 자신을 가리킨다면 해당 bin은 비어있는 것이다. (초기화 과정에서 각 bin의 리스트는 자기 자신을 가리키도록 설정된다.)

  if (in_smallbin_range (nb))

    {

      idx = smallbin_index (nb);

      bin = bin_at (av, idx);


      if ((victim = last (bin)) != bin)

        {

          if (victim == 0) /* initialization check */

            malloc_consolidate (av);

          else

            {

              bck = victim->bk;

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

                {

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

                  goto errout;

                }

              set_inuse_bit_at_offset (victim, nb);

              bin->bk = bck;

              bck->fd = bin;


              if (av != &main_arena)

                victim->size |= NON_MAIN_ARENA;

              check_malloced_chunk (av, victim, nb);

              void *p = chunk2mem (victim);

              alloc_perturb (p, bytes);

              return p;

            }

        }

    }



마지막으로 large요청이 남았죠. large요청은 fasbin을 통합하는것부터 시작합니다.

좀 과도해 보일지라도 fastbin과 관련된 문제들을 피하려면 필요합니다.



  1. 여기까지 왔다면 주어진 크기는 large bin에 속한다. large bin은 바로 찾아보지 않고 다음과 같은 준비 과정을 거친다.
    1.  주어진 크기에 맞는 large bin의 인덱스를 계산하여 idx 지역 변수에 저장한다.
    2.  만약 fast bin을 포함하고 있다면 이들을 모두 병합(consolidate)하여 보다 큰 chunk로 만든다. 이는 큰 메모리 요청을 받은 경우에는 더 이상 작은 크기의 요청이 (최소한 당분간은) 없을 것이라고 가정하기 때문이다. (이로 인해 fast bin으로 인한 fragmentation 문제를 줄일 수 있다.)


  else

    {

      idx = largebin_index (nb);

      if (have_fastchunks (av))

        malloc_consolidate (av);

    }






  1. by http://egloos.zum.com/

-----------------------------------------------

기존에 존재하는 fastbin smallbin을 모두 뒤졌다. 이제는 unsorted bin을 하나씩 뒤지고

꺼낸 unsorted bin을 size에 맞게 bin에 넣는 작업을 하고 없으면 largebin을 뒤지고, 그래도 없으면.. top chunk에서 새로운 메모리를 꺼낼겁니다.


***

최근에 해제되거나 남은 덩어리를 처리하고, 정확히 맞는 경우에만 하나를 취하거나, 작은 요청 인 경우 가장 최근의 정확하지 않은 부분부터 덩어리를 남깁니다. 횡단 된 다른 덩어리를 휴지통에 넣습니다. 이 단계는 청크가 저장소에 배치되는 모든 루틴에서 유일한 위치입니다. 외부 루프는 malloc의 끝까지 우리가 통합해야 할 때까지 깨닫지 못하기 때문에 필요합니다. 그렇게해야하고 다시 시도해야합니다. 이것은 한 번만 발생하며 "작은"요청을 처리하기 위해 메모리를 확장해야하는 경우에만 발생합니다.(구글번역기 좋네요)

***

즉 포인트는 사이즈를 정확하게만 할당한다 이겁니다.

  1. 이제 unsorted bin을 검색하여 일치하는 크기의 free chunk가 있는지 검색한다.
    1.  unsorted bin 내의 가장 오래된 chunk를 victim 지역 변수에 저장한다.
    2.  victim을 unsorted bin의 리스트에서 분리한다. (unsorted bin 내의 chunk들은 오직 한 번만 검사된다.)
    3.  victim의 크기와 주어진 크기가 일치한다면 victim을 반환한다. (종료)
    4.  만약 victim의 unsorted bin 내의 마지막 chunk이고 주어진 크기가 small bin에 속하는 작은 크기이며 victim이 이전 요청을 처리하고 남은 자투리 영역이고 (last_remainder), victim의 크기가 주어진 크기를 처리하고 다른 chunk를 만들만한 여유가 있다면 victim을 분할하여 요청을 처리하고 나머지는 다시 unsorted bin 내에 남겨둔다. (종료) 이는 작은 크기의 연속된 요청이 메모리 상의 연속된 위치에 존재하도록 하여 locality를 높이기 위함이다.
    5.  victim의 크기에 맞는 bin의 리스트의 제일 처음에 삽입한다.
    1. 만약 victim이 large bin에 속한다면 large bin 내의 다른 chunk들과 크기를 비교하여 적절한 위치에 삽입된다.
  1. 이제 large bin에 속하는 경우 해당 리스트를 검사한다. large bin은 (일정 범위 내에서) 크기가 다른 chunk들이 섞여있으므로 현재의 요청을 만족시킬 수 있는 가장 작은 크기의 chunk를 찾아야 한다.
    1.  앞서 계산한 idx에 해당하는 bin의 제일 앞에 있는 chunk를 victim 지역 변수에 저장한다.
    2.  victim의 크기가 주어진 크기보다 큰지 검사하여 그렇지 않다면 탐색을 중지한다. large bin은 앞쪽부터 큰 chunk가 놓이고 뒤로 갈수록 (즉, fd_nextsize 링크를 따라갈수록) 크기가 작아진다. 현재 검사한 victim의 크기는 해당 bin 내에 있는 가장 큰 chunk의 크기이므로 이보다 큰 요청은 해당 bin에서 처리할 수 없다.
    3.  victim을 victim->bk_nextsize로 설정한다. 이제 victim은 해당 bin 내의 가장 작은 크기의 chunk이다.
    4.  victim의 크기가 주어진 크기보다 커질 때까지 victim을 victim->bk_nextsize로 변경한다.
    5.  이제 요청을 처리할 chunk를 찾았다. victim을 리스트에서 분리한다.
    6.  victim의 크기가 요청을 처리하고도 다른 chunk를 구성할 수 있을 정도로 크다면 분할하여 나머지 영역을 chunk로 만들어서 unsorted bin에 추가한다.
    7.  victim을 반환한다. (종료)


  for (;; )

    {

      int iters = 0;

unsorted bin은 FIFO형태의 동작입니다.(first in first out) 아래는 그부분을 보여주는 코드네요

적당한 unsorted_chunk가 나올때까지 계속 뺍니다. 안맞는건 버려버리구요

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

        {

          bck = victim->bk;

          if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)

              || __builtin_expect (victim->size > av->system_mem, 0))

            malloc_printerr (check_action, "malloc(): memory corruption",

                             chunk2mem (victim), av);

          size = chunksize (victim);


small size요청일 경우 unsorted bin에 있는 최근의 bin을 사용합니다. 속도를 신경쓴 부분이죠.

유일하게 size를 정확하게 맞추지 않고 속도를 고려한 부분입니다. 정확한 사이즈의 small chunk가 없을 때만 적용된답니다.


unsorted bin에서 나온 녀석이 요청한 사이즈보다 충분히 클때(remainder chunk(>MINSIZE:청크의 가능한 최소 사이즈)가 나올수 있을정도!) 이녀석을 요청한 size를 할당할 영역으로 쓰기로 합니다. remainder도 싹싹하게 모아두고요


          if (in_smallbin_range (nb) &&

              bck == unsorted_chunks (av) &&

              victim == av->last_remainder &&

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

            {

              /* split and reattach remainder */

unsorted bin은 size가 정확하게 들어맞지는 않으니 남는부분을 정리해야겠죠

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

small_bin이라면 fd_nextsize,bk_nextsize의 영역은 필요없습니다. 이거는 large bin만 가지고 있는 구조체의 값들이죠

              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; 

적절한걸 찾았으니까 뒤에 볼필요도 없겠죠? 바로 return 해버립니다.

            }


       size가 안 맞았던 녀석은 바로 unsorted bin에서 제거합니다.

          unsorted_chunks (av)->bk = bck;

          bck->fd = unsorted_chunks (av);


이제 사이즈가 정확한지도 체크해봅니다.

          if (size == nb)

            {

              set_inuse_bit_at_offset (victim, size);

              if (av != &main_arena)

                victim->size |= NON_MAIN_ARENA;

              check_malloced_chunk (av, victim, nb);

              void *p = chunk2mem (victim);

              alloc_perturb (p, bytes);

              return p;

            }


          /* place chunk in bin */

여기까지 왔다는건 unsorted bin에서 뽑아낸 bin의 size가 적절하지 않았다는 거네요

그럼 꺼낸 bin을 처리하는 과정을 거쳐야합니다.

small size인지 large size인지 구분해서 처리합니다.


          if (in_smallbin_range (size))

            {

              victim_index = smallbin_index (size);

              bck = bin_at (av, victim_index);

              fwd = bck->fd;

            }

          else

            {

              victim_index = largebin_index (size);

              bck = bin_at (av, victim_index);

              fwd = bck->fd;


              /* maintain large bins in sorted order */

              if (fwd != bck)

                {

                  /* Or with inuse bit to speed comparisons */

                  size |= PREV_INUSE;

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

                  assert ((bck->bk->size & NON_MAIN_ARENA) == 0);

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

                    {

                      fwd = bck;

                      bck = bck->bk;


                      victim->fd_nextsize = fwd->fd;

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

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

                    }

                  else

                    {

                      assert ((fwd->size & NON_MAIN_ARENA) == 0);

                      while ((unsigned long) size < fwd->size)

                        {

                          fwd = fwd->fd_nextsize;

                          assert ((fwd->size & NON_MAIN_ARENA) == 0);

                        }


                      if ((unsigned long) size == (unsigned long) fwd->size)

                        /* Always insert in the second position.  */

                        fwd = fwd->fd;

                      else

                        {

                          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;


#define MAX_ITERS       10000

          if (++iters >= MAX_ITERS)

            break;

        }

-------------------------

자 여기 까지가 unsorted bin을 꺼내보는 과정입니다.

여기까지 왔으면 unsorted bin은 비워져 있고 꺼내진 bin들은 자기 size들에 맞게 small이나 large bin으로 들어갔을 겁니다. 아쉽게도 적절한 size를 찾지 못했군요


large size요청이라면 현재 bin들 중에서 요청 size에 적합한 가장 작은 bin을 꺼냅니다. 


  1. 여기까지 왔다면 해당하는 bin 내에서 적당한 chunk를 찾지 못한 것이다. idx 값을 하나 증가시킨 후 더 큰 크기의 bin 내에 free chunk가 있는지 검사한다. (이는 bitmap을 통해 빨리 확인할 수 있다.)
    1.  현재 인덱스에 해당하는 bitmap을 검사하여 free chunk가 있는지 확인한다. 만약 해당 bin이 비어있다면 인덱스를 하나 증가시킨 후 검사를 다시한다. 모든 bitmap을 검사했다면 8번 과정으로 넘어간다.
    2.  bitmap이 설정된 bin이 있다면 해당 bin 내의 (가장 작은 크기의) 가장 오래된 chunk를 victim 지역 변수에 저장한다.
    3.  victim을 리스트에서 분리한다.
    4.  victim의 크기가 요청을 처리하고도 다른 chunk를 구성할 수 있을 정도로 크다면 분할하여 나머지 영역을 chunk로 만들어서 unsorted bin에 추가한다. 나머지 영역의 크기가 small bin에 속한다면 last_remainder 변수가 나머지 영역을 가리키도록 설정한다.
    5.  victim을 반환한다. (종료)



      if (!in_smallbin_range (nb))

        {

          bin = bin_at (av, idx);


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

          if ((victim = first (bin)) != bin &&

              (unsigned long) (victim->size) >= (unsigned long) (nb))

            {

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

              if (victim != last (bin) && victim->size == victim->fd->size)

                victim = victim->fd;


              remainder_size = size - nb;

              unlink (av, victim, bck, fwd);


unlink가 나왔네요 알다싶이 unlink는 중요한 exploit target이죠. largebin이나 smallbin은 fd bk 포인터를 가지고 있어서 unlink가 필요합니다.

쉽게 말하면 bin들은 fwd bck포인터로 연결되어 있는데요(double linked list형태죠) 어떤 bin을 할당하게 되면 연결되어있던 고리가 끊어지겠죠? 이를 다시 연결하는 함수가 unlink입니다.

unlink는 malloc.c의 가장 초반에 나오는데요 꼭 이해하시길 바랍니다.


아래는 remainder를 처리해주는 부분입니다.

              /* Exhaust */

              if (remainder_size < MINSIZE)

                {

                  set_inuse_bit_at_offset (victim, size);

                  if (av != &main_arena)

                    victim->size |= NON_MAIN_ARENA;

                }

              /* Split */

              else

                {

                  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;

            }

        }

---------------------------------------

      /*

         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.

       */

 size가 큰쪽부터 request size보다는 큰 최소의 bin을 찾습니다.

여기서 bitmap을 이용합니다. 영여주석을 따로 지우지 않겠습니다. 한번 읽어보세요


      ++idx;

      bin = bin_at (av, idx);

      block = idx2block (idx);

      map = av->binmap[block];

      bit = idx2bit (idx);


      for (;; )

        {

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

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

            {

              do

                {

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

                    goto use_top;

                }

              while ((map = av->binmap[block]) == 0);


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

              bit = 1;

            }


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

          while ((bit & map) == 0)

            {

              bin = next_bin (bin);

              bit <<= 1;

              assert (bit != 0);

            }


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

            }


          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)

                    victim->size |= NON_MAIN_ARENA;

                }


              /* Split */

              else

                {

                  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;

            }

        }

-------------------------------------

결국 bin에는 쓸만한게 없었습니다.

이제는 bin에서 할당할게 아니라 아예 동적할당영역의 끝부분에서새롭게 할당해 줄겁니다. 


  1. 여기까지 왔다면 bin 내에 모든 chunk가 비어있거나 요청을 만족할 수 없는 상황이다. 이제 top chunk를 사용한다.
    1.  만약 top chunk의 크기가 주어진 요청을 만족하고도 새로운 chunk를 만들 수 있는 크기라면 분할하여 victim을 반환하고 나머지 영역을 top chunk로 설정한다. (종료)
    2.  그렇지 않고 주어진 크기가 small bin 영역에 속한다면 fast bin이 남아있을 것이다. fast bin을 병합한 후 다시 2번 과정으로 돌아가서 할당을 시도한다.
    3.  그렇지 않다면 시스템의 heap 영역을 늘려야 한다. 이는 sYSMALLOc() 함수가 처리하며, 이 함수의 반환값을 반환하고 종료한다.


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


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

      else if (have_fastchunks (av))

        {

          malloc_consolidate (av);

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

        }

    }

}


    1.  


'시스템 > 시스템 해킹' 카테고리의 다른 글

ulimit  (0) 2017.02.19
glibc malloc.c의 free함수 분석 약간의 exploit관점(작성중)  (0) 2017.02.09
버퍼오버플로우를 시작하며  (1) 2017.02.05
별건 아니고 malloc.c  (0) 2017.01.30
angr 정리  (0) 2017.01.25

+ Recent posts