free

Unterstützen Sie HackTricks

Zusammenfassung der Freigabe-Bestellung

(In dieser Zusammenfassung werden keine Überprüfungen erläutert und einige Fälle wurden aus Gründen der Kürze ausgelassen)

  1. Wenn die Adresse null ist, wird nichts unternommen

  2. Wenn der Chunk gemappt wurde, wird er entmappt und beendet

  3. Rufen Sie _int_free auf:

  4. Fügen Sie den Chunk bei Möglichkeit zum Tcache hinzu

  5. Fügen Sie den Chunk bei Möglichkeit zum Fast Bin hinzu

  6. Rufen Sie _int_free_merge_chunk auf, um den Chunk zu konsolidieren, falls erforderlich, und fügen Sie ihn zur ungeordneten Liste hinzu

__libc_free

Free ruft __libc_free auf.

  • Wenn die übergebene Adresse Null (0) ist, wird nichts unternommen.

  • Überprüfen Sie den Zeiger-Tag

  • Wenn der Chunk gemappt ist, entmappt ihn und das war's

  • Wenn nicht, fügen Sie die Farbe hinzu und rufen Sie _int_free darüber auf

__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 start <a href="#int_free" id="int_free"></a>

Es beginnt mit einigen Überprüfungen, um sicherzustellen, dass:

* der **Zeiger** **ausgerichtet** ist oder einen Fehler auslöst `free(): ungültiger Zeiger`
* die **Größe** nicht kleiner als das Minimum ist und dass die **Größe** auch **ausgerichtet** ist oder einen Fehler auslöst: `free(): ungültige Größe`

<details>

<summary>_int_free start</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

Zuerst wird versucht, diesen Chunk im zugehörigen tcache zu allozieren. Es werden jedoch einige Überprüfungen zuvor durchgeführt. Es wird durch alle Chunks des tcaches in demselben Index wie der freigegebene Chunk iteriert und:

  • Wenn es mehr Einträge gibt als mp_.tcache_count: free(): zu viele Chunks im tcache erkannt

  • Wenn der Eintrag nicht ausgerichtet ist: free(): nicht ausgerichteter Chunk im tcache 2 erkannt

  • Wenn der freigegebene Chunk bereits freigegeben wurde und als Chunk im tcache vorhanden ist: free(): doppelt freigegebener Chunk im tcache 2 erkannt

Wenn alles gut läuft, wird der Chunk dem tcache hinzugefügt und die Funktion kehrt zurück.

_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 Fast Bin <a href="#int_free" id="int_free"></a>

Beginnen Sie damit zu überprüfen, ob die Größe für den Fast Bin geeignet ist, und prüfen Sie, ob es möglich ist, sie nahe am Top Chunk zu platzieren.

Fügen Sie dann den freigegebenen Chunk oben im Fast Bin hinzu und führen Sie einige Überprüfungen durch:

- Wenn die Größe des Chunks ungültig ist (zu groß oder zu klein), wird ausgelöst: `free(): invalid next size (fast)`
- Wenn der hinzugefügte Chunk bereits oben im Fast Bin war: `double free or corruption (fasttop)`
- Wenn die Größe des Chunks oben eine andere Größe als der hinzugefügte Chunk hat: `invalid fastbin entry (free)`

<details>

<summary>_int_free Fast 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 Finale

Wenn der Chunk noch nicht in einem Bin allokiert wurde, rufen Sie _int_free_merge_chunk auf.

Last updated