Profile

youngsouk

youngsouk

_int_free 분석

static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
  INTERNAL_SIZE_T size;        /* free시킬 크기 */
  mfastbinptr *fb;             /* 관련된 fastbin */
  mchunkptr nextchunk;         /* 인접한 청크 */
  INTERNAL_SIZE_T nextsize;    /* 인접한 청크의 크기 */
  int nextinuse;               /* 물리적으로 다음 청크가 사용줄일 때 셋팅 */
  INTERNAL_SIZE_T prevsize;    /* 물리적으로 이전 청크의 크기 */
  mchunkptr bck;               /* 링킹을 위한 임시 변수 */
  mchunkptr fwd;               /* 링킹을 위한 임시 변수 */

  size = chunksize (p); // free 시킬 청크의 크기를 저장한다.

  /* 성능을 해치지 않는 보안검사: 할당자는 절대로 주소 공간의 끝부분에 할당하지 않는다. 
  	그러므로 우리는 우연히 나타나거나 간섭자들에 의해 조작된 몇몇 크기 값들을 결정할 수 있다.*/
    
  if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)// 청크의 포인터가 정상인지 검사
      || __builtin_expect (misaligned_chunk (p), 0))
    malloc_printerr ("free(): invalid pointer");
  /* 우리는 각 청크가 최소 크기를 가지고 있고, MALLLOC_ALIGNMENT(2 * sizeof(size_t))의 
  	배수라는 것을 안다.*/
  if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
    malloc_printerr ("free(): invalid size");

  check_inuse_chunk(av, p); // 물리적으로 다음 청크의 prev_in_use 비트를 확인한다.

#if USE_TCACHE // TCACHE를 사용할 때 tcache에 넣을 수 있으면 바로 넣는다.
  {
    size_t tc_idx = csize2tidx (size);

    if (tcache
	&& tc_idx < mp_.tcache_bins
	&& tcache->counts[tc_idx] < mp_.tcache_count)
      {
	tcache_put (p, tc_idx);
	return;
      }
  }
#endif

  /*
    fastbin에 넣을 크기라면, fastbin에 넣는다. 그래서 이것은 malloc에서 바로 발견되고, 빠르게 사용된다.
  */

	 // fastbin에 들어갈 크기인지 검사
  if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())

#if TRIM_FASTBINS
      /*
	만약 TRIM_FASTBINS이 셋팅되어 있다면, top 청크에 인접한 청크를 fastbin에 두지 않는다.
      */
      && (chunk_at_offset(p, size) != av->top)
#endif
      ) {

	// 물리적으로 다음청크의 크기가 정상적인지 검사
    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))
      {
	bool fail = true;
	/* 우린는 이 시점에서 잠금이 걸려있지 않고 시스템 메모리에서 동시에 발생하는 변조는 거짓된 참값을 유발한다. 
	잠금을 한 뒤에 검사를 나중에 다시 한다.  */
	if (!have_lock) // 잠금이 걸려있지 않으면
	  {
	    __libc_lock_lock (av->mutex); // 잠금을 한다.
	    fail = (chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ // 청크 크기 검사
		    || chunksize (chunk_at_offset (p, size)) >= av->system_mem);
	    __libc_lock_unlock (av->mutex); // 잠금 해제한다.
	  }

	if (fail) // 청크 크기 검사에 실패하면
	  malloc_printerr ("free(): invalid next size (fast)");
      }

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

    atomic_store_relaxed (&av->have_fastchunks, true);
    unsigned int idx = fastbin_index(size); // fastbin index
    fb = &fastbin (av, idx); // index에 맞는 fastbin주소 반환

    /* fastbin의 single linked list를 구성. P->FD = *FB; *FB = P;  */
    mchunkptr old = *fb, old2; // 가장 최근에 free 했던 청크 저장

    if (SINGLE_THREAD_P) //단일 스레드일 때
      {
	/* bin의 가장 위에 있는 청크(가장 최근에 free한 청크)가 우리가 free할 청크인지 확인한다.
    (ex. double free bug) */
	if (__builtin_expect (old == p, 0)) // 가장 최근에 free 했던 청크가 free 시킬 청크랑 같다면
	  malloc_printerr ("double free or corruption (fasttop)");
	p->fd = old;
	*fb = p;
      }
    else //단일 스레드가 아니면
      do
	{
	  /* bin의 가장 위에 있는 청크(가장 최근에 free한 청크)가 우리가 free할 청크인지 확인한다.
	     (ex. double free bug)*/
	  if (__builtin_expect (old == p, 0))
	    malloc_printerr ("double free or corruption (fasttop)");
	  p->fd = old2 = old;
	}
      while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2))
	     != old2);

    /* fastbin에 가장 나중에 들어간 청크의 크기가 우리가 추가할 청크의 크기와 같은지 확인한다.
       우리는 잠금이 걸려있을때만 0LD를 역참조 할 수 있다. 그렇지 않으면, 이미 한번 할당되었을 것이다.
   */
    if (have_lock && old != NULL //
	&& __builtin_expect (fastbin_index (chunksize (old)) != idx, 0))
      malloc_printerr ("invalid fastbin entry (free)");
  }

  /*
    다른 mmap으로 할당되지 않은 청크들을 도착하면 병합한다. 
  */

  else if (!chunk_is_mmapped(p)) {

    /* 단일 스레드면 아레나를 잠그지 않는다. */
    if (SINGLE_THREAD_P)
      have_lock = true;

    if (!have_lock) //잠금이 안걸려 있으면 잠금을 건다.
      __libc_lock_lock (av->mutex);

    nextchunk = chunk_at_offset(p, size); //물리적으로 다음 청크의 주소를 저장한다.

    /* 가벼운 검사: 그 블럭이 이미 top chunk인지 확인 */
    if (__glibc_unlikely (p == av->top)) // top chunk를 free하려 할 때
      malloc_printerr ("double free or corruption (top)");
    /* 또는 물리적으로 다음 청크가 아레나의 경계 밖에 있는지 확인한다. */
    if (__builtin_expect (contiguous (av)
			  && (char *) nextchunk
			  >= ((char *) av->top + chunksize(av->top)), 0))
	malloc_printerr ("double free or corruption (out)");
    /* 또는 그 블럭이 실제로 쓰지 않는다고 되어있는지 확인한다.*/
    if (__glibc_unlikely (!prev_inuse(nextchunk)))
      malloc_printerr ("double free or corruption (!prev)");

    nextsize = chunksize(nextchunk); //다음 청크의 크기 저장&다음 청크의 크기가 정상인지 검사
    if (__builtin_expect (chunksize_nomask (nextchunk) <= 2 * SIZE_SZ, 0)
	|| __builtin_expect (nextsize >= av->system_mem, 0))
      malloc_printerr ("free(): invalid next size (normal)");

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

    /* 이전 청크가 free된 상태이면 뒤의 청크를 병합시킨다. */
    if (!prev_inuse(p)) {
      prevsize = prev_size (p);
      size += prevsize;
      p = chunk_at_offset(p, -((long) prevsize));
      if (__glibc_unlikely (chunksize(p) != prevsize))
        malloc_printerr ("corrupted size vs. prev_size while consolidating");
      unlink(av, p, bck, fwd);
    }

    if (nextchunk != av->top) { //다음 청크가 top chunk가 아니면
      /* inuse bit를 얻어온다. */
      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

      /* 다음 청크와 병합시킨다.*/
      if (!nextinuse) {
	unlink(av, nextchunk, bck, fwd);
	size += nextsize;
      } else
	clear_inuse_bit_at_offset(nextchunk, 0);

      /*
      그 청크를 unsorted bin에 넣는다. 청크들은 그들이 malloc에서 1번
      재할당될 기회가 주어지기 전까지는 보통 bins에 들어가지 않는다.
      */

	 /* unsorted bin에 넣는 과정 */
      bck = unsorted_chunks(av);
      fwd = bck->fd;
      if (__glibc_unlikely (fwd->bk != bck))
	malloc_printerr ("free(): corrupted unsorted chunks");
      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와 병합한다.
    */

    else {
      size += nextsize;
      set_head(p, size | PREV_INUSE);
      av->top = p;
      check_chunk(av, p);
    }

    /*
      만약 큰 공간을 free시킨다면, 인접한 청크를 병합시킨다.그래서 만약 사용되지 않는 메모리의 총량이 
      trim_threshold를 넘는다면, malloc_trim에게 top chunk를 줄이도록 요청한다.
      max_fast가 0이 아니라면, 우리는 top chunk에 인접한 fastbins가 있는지 모른다. 그래서
      우리는 fastbins가 병합되지 않는한, 한계가 되어있는지 말할 수 없다. 하지만
      우리는 각각이 free될 때마다 병합되는 것을 원치 않는다.  절충안으로서, 병합은 
      FASTBIN_CONSOLIDATION_THRESHOLD에 도달할 때 행해진다.
    */

    if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {
      if (atomic_load_relaxed (&av->have_fastchunks))
	malloc_consolidate(av);

      if (av == &main_arena) { // main_arena에 속해있는 청크라면
#ifndef MORECORE_CANNOT_TRIM
	if ((unsigned long)(chunksize(av->top)) >= //top chunk 크기가 한계를 초과하면
	    (unsigned long)(mp_.trim_threshold))
	  systrim(mp_.top_pad, av);
#endif
      } else {
	/*  top chunk가 크지 않을지라도, 항상 heap_trim()함수를 시도한다. 왜냐하면
    	그에 상응하는 heap이 나가기 떄문이다.
    */
	heap_info *heap = heap_for_ptr(top(av));

	assert(heap->ar_ptr == av);
	heap_trim(heap, mp_.top_pad);
      }
    }

    if (!have_lock) //잠금이 안되어 있다면
      __libc_lock_unlock (av->mutex);
  }
  /*
  	그 청크가 mmap을 통해 할당되어있다면 munmap을 통해 해제시킨다.
  */

  else {
    munmap_chunk (p);
  }
}

'힙(heap) > glibc' 카테고리의 다른 글

sysmalloc() 분석(코드 분석&&순서도)  (0) 2019.08.30
_int_malloc함수 분석(3)  (0) 2019.08.12
_int_malloc 함수 분석 (2)  (0) 2019.08.09
_int_malloc 함수 분석 (1)  (0) 2019.08.09
__libc_free함수 분석  (0) 2019.08.09