free

HackTricks'i Destekleyin

Ücretsiz Sipariş Özeti

(Bu özetde hiçbir kontrol açıklanmamıştır ve kısa olması için bazı durumlar atlanmıştır)

  1. Adres null ise hiçbir şey yapma

  2. Chunk mmap edilmişse, onu munmap et ve işlemi bitir

  3. _int_free'yi çağır:

  4. Mümkünse, chunk'ı tcache'e ekle

  5. Mümkünse, chunk'ı fast bin'e ekle

  6. Gerekirse chunk'ı birleştirmek için _int_free_merge_chunk'ı çağır ve onu sırasız listeye ekle

__libc_free

Free, __libc_free'yi çağırır.

  • Geçilen adres Null (0) ise hiçbir şey yapma.

  • İşaretçi etiketini kontrol et

  • Chunk mmap edilmişse, munmap et ve işlemi bitir

  • Değilse, renk ekle ve üzerine _int_free çağır

__lib_free kodu

```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 başlangıç <a href="#int_free" id="int_free"></a>

Belirli kontrollerle başlar:

* **İşaretçinin** **hizalandığından** emin olur veya hata tetikler `free(): geçersiz işaretçi`
* **Boyutun** minimumdan küçük olmadığından ve **boyutunun** de **hizalandığından** emin olur veya hata tetikler: `free(): geçersiz boyut`

<details>

<summary>_int_free başlangıç</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

İlk olarak bu parçayı ilgili tcache'e ayırmaya çalışacaktır. Ancak önceden bazı kontroller yapılır. Serbest bırakılan parçanın aynı dizindeki tüm tcache parçaları üzerinden dönecektir ve:

  • Giriş sayısı mp_.tcache_count'dan fazlaysa: free(): tcache'de çok fazla parça tespit edildi

  • Giriş hizalanmamışsa: free(): tcache 2'de hizalanmamış parça tespit edildi

  • Serbest bırakılan parça zaten serbest bırakılmış ve tcache'de bir parça olarak mevcutsa: free(): tcache 2'de çift serbest bırakma tespit edildi

Her şey yolunda giderse, parça tcache'e eklenir ve işlev geri döner.

_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 hızlı bin <a href="#int_free" id="int_free"></a>

Öncelikle, boyutun hızlı bin için uygun olup olmadığını kontrol edin ve onu üst parça ile yakın bir şekilde ayarlayabilir miyiz kontrol edin.

Ardından, serbest bırakılan parçayı hızlı bini en üste ekleyin ve bazı kontroller yaparken:

- Parçanın boyutu geçersiz ise (çok büyük veya küçük) tetikleyin: `free(): invalid next size (fast)`
- Eklenen parça zaten hızlı binin en üstünde ise: `double free or corruption (fasttop)`
- Üstteki parçanın boyutu eklediğimiz parçanın boyutundan farklı ise: `invalid fastbin entry (free)`

<details>

<summary>_int_free Hızlı Bin</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 finali

Eğer blok henüz herhangi bir kovada ayrılmamışsa, _int_free_merge_chunk fonksiyonunu çağırın.

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

Bu işlev, P boyutundaki parçayı komşularıyla birleştirmeye çalışacaktır. Sonuçta oluşan parçayı sıralanmamış kova listesine koyar.

Bazı kontroller yapılır:

* Eğer parça en üst parça ise: `double free or corruption (top)`
* Eğer bir sonraki parça arena sınırlarının dışında ise: `double free or corruption (out)`
* Eğer parça kullanılmış olarak işaretlenmemişse (`prev_inuse`'da bulunan bir sonraki parçada): `double free or corruption (!prev)`
* Eğer bir sonraki parçanın boyutu çok küçük veya çok büyükse: `free(): invalid next size (normal)`
* Eğer önceki parça kullanılmıyorsa, birleştirmeye çalışacaktır. Ancak, eğer prev\_size önceki parçada belirtilen boyuttan farklıysa: `corrupted size vs. prev_size while consolidating`

<details>

<summary>_int_free_merge_chunk kodu</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);
}
HackTricks'i Destekleyin

Last updated