[heap] fastbin_dup

dandb3·2024년 5월 9일
0

pwnable

목록 보기
20/25

Double Free Bug를 발생시킬 수 있는 방법 중 하나인 fastbin_dup에 대해서 알아보자.

이름으로 부터 알 수 있듯이, fastbin 내부에 같은 주소를 가진 chunk 여러 개를 넣어서 double free를 발생시키는 원리이다.

_int_free에서의 해당부분 코드를 보자. (libc 2.34 기준)

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

#if TRIM_FASTBINS
      /*
	If TRIM_FASTBINS set, don't place chunks
	bordering top into fastbins
      */
      && (chunk_at_offset(p, size) != av->top)
#endif
      ) {

    if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))
			  <= CHUNK_HDR_SZ, 0)
	|| __builtin_expect (chunksize (chunk_at_offset (p, size))
			     >= av->system_mem, 0))
      {
	bool fail = true;
	/* We might not have a lock at this point and concurrent modifications
	   of system_mem might result in a false positive.  Redo the test after
	   getting the lock.  */
	if (!have_lock)
	  {
	    __libc_lock_lock (av->mutex);
	    fail = (chunksize_nomask (chunk_at_offset (p, size)) <= CHUNK_HDR_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 - CHUNK_HDR_SZ);

    atomic_store_relaxed (&av->have_fastchunks, true);
    unsigned int idx = fastbin_index(size);
    fb = &fastbin (av, idx);

    /* Atomically link P to its fastbin: P->FD = *FB; *FB = P;  */
    mchunkptr old = *fb, old2;

    if (SINGLE_THREAD_P)
      {
	/* Check that the top of the bin is not the record we are going to
	   add (i.e., double free).  */
	if (__builtin_expect (old == p, 0))
	  malloc_printerr ("double free or corruption (fasttop)");
	p->fd = PROTECT_PTR (&p->fd, old);
	*fb = p;
      }
    else
      do
	{
	  /* Check that the top of the bin is not the record we are going to
	     add (i.e., double free).  */
	  if (__builtin_expect (old == p, 0))
	    malloc_printerr ("double free or corruption (fasttop)");
	  old2 = old;
	  p->fd = PROTECT_PTR (&p->fd, old);
	}
      while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2))
	     != old2);

    /* 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
       allocated again.  */
    if (have_lock && old != NULL
	&& __builtin_expect (fastbin_index (chunksize (old)) != idx, 0))
      malloc_printerr ("invalid fastbin entry (free)");
  }

중간에

	if (__builtin_expect (old == p, 0))
	  malloc_printerr ("double free or corruption (fasttop)");

이 부분이 바로 double-free를 체크하는 부분이다.
만약에 현재 fastbin의 제일 윗부분 chunk 주소와 현재 free하는 chunk의 주소가 같다면 double-free로 인식해서 에러를 발생시킨다.

여기서 주목할 점은, double free 체크를 할 때 가장 최근에 free된 chunk의 주소만 비교한다는 점이다.
즉, chunk1, chunk2가 있을 때, free(chunk1) -> free(chunk2) -> free(chunk1) 이런 식으로 호출하면 위 검증을 우회할 수 있게 된다.

profile
공부 내용 저장소

0개의 댓글