free

htARTE(HackTricks AWS Red Team Expert)からゼロからヒーローまでAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)を学ぶ!

HackTricksをサポートする他の方法:

Free Order Summary

(このサマリーではチェックは説明されておらず、簡潔さのため一部のケースが省略されています)

  1. アドレスがnullの場合は何も行いません

  2. チャンクがmmapedされている場合は、mummapして終了します

  3. _int_freeを呼び出します:

  4. 可能であれば、チャンクをtcacheに追加します

  5. 可能であれば、チャンクをfast binに追加します

  6. 必要に応じてチャンクを統合し、未整列リストに追加するために_int_free_merge_chunkを呼び出します

__libc_free

Free__libc_freeを呼び出します。

  • 渡されたアドレスがNull(0)の場合は何も行いません。

  • ポインタータグをチェックします

  • チャンクがmmapedされている場合は、mummapして終了します

  • そうでない場合は、カラーを追加して_int_freeを呼び出します

__lib_free code

```c void __libc_free (void *mem) { mstate ar_ptr; mchunkptr p; /* chunk corresponding to mem */

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

/* Quickly check that the freed pointer matches the tag for the memory. This gives a useful double-free detection. */ if (__glibc_unlikely (mtag_enabled)) *(volatile char *)mem;

int err = errno;

p = mem2chunk (mem);

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) { 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); } else { MAYBE_INIT_TCACHE ();

/* Mark the chunk as belonging to the library again. */ (void)tag_region (chunk2mem (p), memsize (p));

ar_ptr = arena_for_chunk (p); _int_free (ar_ptr, p, 0); }

__set_errno (err); } libc_hidden_def (__libc_free)

</details>

## \_int\_free <a href="#int_free" id="int_free"></a>

### \_int\_free 開始 <a href="#int_free" id="int_free"></a>

いくつかのチェックを行って開始します:

- **ポインタ** が **アラインされている**ことを確認し、そうでない場合はエラー `free(): invalid pointer` をトリガーします
- **サイズ** が最小値より小さくないこと、かつ **サイズ** も **アラインされている**ことを確認し、そうでない場合はエラー `free(): invalid size` をトリガーします

<details>

<summary>_int_free 開始</summary>
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4493C1-L4513C28

#define aligned_OK(m) (((unsigned long) (m) &MALLOC_ALIGN_MASK) == 0)

static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
INTERNAL_SIZE_T size;        /* its size */
mfastbinptr *fb;             /* associated fastbin */

size = chunksize (p);

/* Little security check which won't hurt performance: the
allocator never wraps 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.  */
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
malloc_printerr ("free(): invalid pointer");
/* We know that each chunk is at least MINSIZE bytes in size or a
multiple of MALLOC_ALIGNMENT.  */
if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
malloc_printerr ("free(): invalid size");

check_inuse_chunk(av, p);

_int_free tcache

最初に、このチャンクを関連する tcache に割り当てようとします。ただし、以前にいくつかのチェックが実行されます。解放されたチャンクと同じインデックスの tcache のすべてのチャンクをループし、次のようにします。

  • エントリが mp_.tcache_count よりも多い場合: free(): too many chunks detected in tcache

  • エントリがアラインされていない場合: free(): unaligned chunk detected in tcache 2

  • 解放されたチャンクがすでに解放されており、tcache にチャンクとして存在する場合: free(): double free detected in tcache 2

すべてうまくいけば、チャンクは tcache に追加され、関数が返されます。

_int_free tcache

```c // From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4515C1-L4554C7 #if USE_TCACHE { size_t tc_idx = csize2tidx (size); if (tcache != NULL && tc_idx < mp_.tcache_bins) { /* Check to see if it's already in the tcache. */ tcache_entry *e = (tcache_entry *) chunk2mem (p);

/* This test succeeds on double free. However, we don't 100% trust it (it also matches random payload data at a 1 in 2^<size_t> chance), so verify it's not an unlikely coincidence before aborting. / if (_glibc_unlikely (e->key == tcache_key)) { tcache_entry *tmp; size_t cnt = 0; LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx); for (tmp = tcache->entries[tc_idx]; tmp; tmp = REVEAL_PTR (tmp->next), ++cnt) { if (cnt >= mp.tcache_count) malloc_printerr ("free(): too many chunks detected in tcache"); if (__glibc_unlikely (!aligned_OK (tmp))) malloc_printerr ("free(): unaligned chunk detected in tcache 2"); if (tmp == e) malloc_printerr ("free(): double free detected in tcache 2"); / If we get here, it was a coincidence. We've wasted a few cycles, but don't abort. */ } }

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

</details>

### \_int\_free ファストビン <a href="#int_free" id="int_free"></a>

まず、サイズがファストビンに適しているかどうかを確認し、トップチャンクに近づけることが可能かどうかをチェックします。

次に、いくつかのチェックを行いながら、解放されたチャンクをファストビンのトップに追加します。

- チャンクのサイズが無効(大きすぎるか小さすぎる)である場合は、トリガーを作動させます: `free(): invalid next size (fast)`
- 追加されたチャンクがすでにファストビンのトップである場合: `double free or corruption (fasttop)`
- トップのチャンクのサイズが、追加しようとしているチャンクのサイズと異なる場合: `invalid fastbin entry (free)`

<details>

<summary>_int_free ファストビン</summary>
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4556C2-L4631C4

/*
If eligible, place chunk on a fastbin so it can be found
and used quickly in malloc.
*/

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

_int_free 最終処理

もしチャンクがまだどのビンにも割り当てられていない場合は、_int_free_merge_chunkを呼び出します。

```c /* Consolidate other non-mmapped chunks as they arrive. */

else if (!chunk_is_mmapped(p)) {

/* If we're single-threaded, don't lock the arena. */ if (SINGLE_THREAD_P) have_lock = true;

if (!have_lock) __libc_lock_lock (av->mutex);

_int_free_merge_chunk (av, p, size);

if (!have_lock) __libc_lock_unlock (av->mutex); } /* If the chunk was allocated via mmap, release via munmap(). */

else { munmap_chunk (p); } }

</details>

## \_int\_free\_merge\_chunk

この関数は、サイズがSIZEバイトのチャンクPをその隣接するチャンクとマージしようとします。結果のチャンクを未整列のビンリストに配置します。

いくつかのチェックが実行されます:

- チャンクがトップチャンクである場合: `double free or corruption (top)`
- 次のチャンクがアリーナの境界を超えている場合: `double free or corruption (out)`
- チャンクが使用中としてマークされていない場合(次のチャンクの`prev_inuse`に): `double free or corruption (!prev)`
- 次のチャンクのサイズが小さすぎるか大きすぎる場合: `free(): invalid next size (normal)`
- 前のチャンクが使用中でない場合、統合を試みます。ただし、prev_sizeが前のチャンクで示されているサイズと異なる場合: `corrupted size vs. prev_size while consolidating`

<details>

<summary>_int_free_merge_chunk code</summary>
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4660C1-L4702C2

/* Try to merge chunk P of SIZE bytes with its neighbors.  Put the
resulting chunk on the appropriate bin list.  P must not be on a
bin list yet, and it can be in use.  */
static void
_int_free_merge_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T size)
{
mchunkptr nextchunk = chunk_at_offset(p, size);

/* Lightweight tests: check whether the block is already the
top block.  */
if (__glibc_unlikely (p == av->top))
malloc_printerr ("double free or corruption (top)");
/* Or whether the next chunk is beyond the boundaries of the arena.  */
if (__builtin_expect (contiguous (av)
&& (char *) nextchunk
>= ((char *) av->top + chunksize(av->top)), 0))
malloc_printerr ("double free or corruption (out)");
/* Or whether the block is actually not marked used.  */
if (__glibc_unlikely (!prev_inuse(nextchunk)))
malloc_printerr ("double free or corruption (!prev)");

INTERNAL_SIZE_T nextsize = chunksize(nextchunk);
if (__builtin_expect (chunksize_nomask (nextchunk) <= CHUNK_HDR_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
malloc_printerr ("free(): invalid next size (normal)");

free_perturb (chunk2mem(p), size - CHUNK_HDR_SZ);

/* Consolidate backward.  */
if (!prev_inuse(p))
{
INTERNAL_SIZE_T 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_chunk (av, p);
}

/* Write the chunk header, maybe after merging with the following chunk.  */
size = _int_free_create_chunk (av, p, size, nextchunk, nextsize);
_int_free_maybe_consolidate (av, size);
}
ゼロからヒーローまでのAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)

HackTricksをサポートする他の方法:

Last updated