free

Υποστηρίξτε το HackTricks

Δωρεάν Περίληψη Παραγγελίας

(Δεν εξηγούνται ελέγχοι σε αυτήν την περίληψη και ορισμένες περιπτώσεις έχουν παραλειφθεί για συντομία)

  1. Αν η διεύθυνση είναι null, μην κάνετε τίποτα

  2. Αν το κομμάτι ήταν διαμορφωμένο με χρήση mmap, απελευθερώστε το και τελειώστε

  3. Καλέστε _int_free:

  4. Αν είναι δυνατό, προσθέστε το κομμάτι στην tcache

  5. Αν είναι δυνατό, προσθέστε το κομμάτι στο fast bin

  6. Καλέστε το _int_free_merge_chunk για να συγχωνεύσετε το κομμάτι αν είναι απαραίτητο και προσθέστε το στη λίστα των μη ταξινομημένων

__libc_free

Το Free καλεί το __libc_free.

  • Αν η διεύθυνση που περνάται είναι Null (0), μην κάνετε τίποτα.

  • Ελέγξτε το ετικέτα δείκτη

  • Αν το κομμάτι είναι mmaped, απελευθερώστε το και τέλος

  • Αν όχι, προσθέστε το χρώμα και καλέστε το _int_free πάνω του

Κώδικας __lib_free

```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(): μη έγκυρος δείκτης`
* ότι το **μέγεθος** δεν είναι μικρότερο από το ελάχιστο και ότι το **μέγεθος** είναι επίσης **ευθυγραμμισμένο** ή εκκινείται σφάλμα: `free(): μη έγκυρο μέγεθος`

<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 και η συνάρτηση επιστρέφει.

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

_int_free γρήγορος κάδος

Ξεκινήστε ελέγχοντας αν το μέγεθος είναι κατάλληλο για τον γρήγορο κάδο και ελέγξτε αν είναι δυνατόν να το τοποθετήσετε κοντά στο κομμάτι κορυφής.

Στη συνέχεια, προσθέστε το απελευθερωμένο κομμάτι στην κορυφή του γρήγορου κάδου ενώ πραγματοποιείτε ορισμένους ελέγχους:

  • Εάν το μέγεθος του κομματιού είναι μη έγκυρο (πολύ μεγάλο ή μικρό) ενεργοποιήστε: free(): invalid next size (fast)

  • Εάν το προστιθέμενο κομμάτι ήταν ήδη στην κορυφή του γρήγορου κάδου: double free or corruption (fasttop)

  • Εάν το μέγεθος του κομματιού στην κορυφή έχει διαφορετικό μέγεθος από το κομμάτι που προσθέτουμε: invalid fastbin entry (free)

_int_free Fast Bin

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

Αν το κομμάτι δεν είχε ακόμα εκχωρηθεί σε κάποιο bin, καλέστε την `_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);
}
}

_int_free_merge_chunk

Αυτή η συνάρτηση θα προσπαθήσει να συγχωνεύσει το κομμάτι P με μέγεθος SIZE bytes με τους γείτονές του. Θα τοποθετήσει το αποτέλεσμα στη λίστα unsorted bin.

Κάποιοι έλεγχοι πραγματοποιούνται:

  • Αν το κομμάτι είναι το κορυφαίο κομμάτι: 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

_int_free_merge_chunk code

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

</details>

<div data-gb-custom-block data-tag="hint" data-style='success'>

Μάθετε & εξασκηθείτε στο Hacking του AWS: <img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**Εκπαίδευση HackTricks AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
Μάθετε & εξασκηθείτε στο Hacking του GCP: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**Εκπαίδευση HackTricks GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)

<details>

<summary>Υποστηρίξτε το HackTricks</summary>

* Ελέγξτε τα [**σχέδια συνδρομής**](https://github.com/sponsors/carlospolop)!
* **Εγγραφείτε** 💬 στην [**ομάδα Discord**](https://discord.gg/hRep4RUj7f) ή στην [**ομάδα telegram**](https://t.me/peass) ή **ακολουθήστε** μας στο **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα** [**HackTricks**](https://github.com/carlospolop/hacktricks) και [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) αποθετήρια στο GitHub.

</details>

</div>

Last updated