free

Ucz się i praktykuj Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Ucz się i praktykuj Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Wesprzyj HackTricks

Podsumowanie darmowego zamówienia

(W tym podsumowaniu nie są wyjaśnione żadne kontrole, a niektóre przypadki zostały pominięte dla zwięzłości)

  1. Jeśli adres jest null, nie rób nic

  2. Jeśli kawałek był zmapowany, odmapuj go i zakończ

  3. Wywołaj _int_free:

  4. Jeśli to możliwe, dodaj kawałek do tcache

  5. Jeśli to możliwe, dodaj kawałek do fast bin

  6. Wywołaj _int_free_merge_chunk, aby skonsolidować kawałek, jeśli jest to konieczne, i dodaj go do listy nieuporządkowanej

__libc_free

Free wywołuje __libc_free.

  • Jeśli przekazany adres to Null (0), nie rób nic.

  • Sprawdź tag wskaźnika

  • Jeśli kawałek jest zmapowany, odmapuj go i to wszystko

  • Jeśli nie, dodaj kolor i wywołaj _int_free nad nim

Kod __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>

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

Zaczyna się od sprawdzenia, czy:

* **wskaźnik** jest **wyrównany**, lub wywołuje błąd `free(): invalid pointer`
* **rozmiar** nie jest mniejszy niż minimalny i czy **rozmiar** jest również **wyrównany**, lub wywołuje błąd: `free(): invalid size`

<details>

<summary>Rozpoczęcie \_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

Najpierw spróbuje zaalokować ten kawałek w powiązanym tcache. Jednakże, wcześniej wykonane są pewne sprawdzenia. Przeiteruje przez wszystkie kawałki tcache o tym samym indeksie co zwolniony kawałek i:

  • Jeśli jest więcej wpisów niż mp_.tcache_count: free(): wykryto zbyt wiele kawałków w tcache

  • Jeśli wpis nie jest wyrównany: free(): wykryto niezrównany kawałek w tcache 2

  • jeśli zwolniony kawałek był już zwolniony i jest obecny jako kawałek w tcache: free(): wykryto podwójne zwolnienie w tcache 2

Jeśli wszystko pójdzie dobrze, kawałek zostanie dodany do tcache i funkcja zwróci.

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

Zacznij od sprawdzenia, czy rozmiar jest odpowiedni dla szybkiego bloku i sprawdź, czy można go ustawić blisko najwyższego fragmentu.

Następnie dodaj zwolniony fragment na górze szybkiego bloku, wykonując pewne kontrole:

  • Jeśli rozmiar fragmentu jest nieprawidłowy (zbyt duży lub mały), wywołaj: free(): invalid next size (fast)

  • Jeśli dodany fragment był już na górze szybkiego bloku: double free or corruption (fasttop)

  • Jeśli rozmiar fragmentu na górze ma inny rozmiar niż dodawany fragment: invalid fastbin entry (free)

_int_free Szybki Blok

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

Jeśli kawałek nie był jeszcze przydzielony do żadnego kubełka, wywołaj `_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

Ta funkcja spróbuje połączyć kawałek P o rozmiarze SIZE bajtów z jego sąsiadami. Umieść wynikowy kawałek na liście nieuporządkowanej skrzynki.

Wykonywane są pewne sprawdzenia:

  • Jeśli kawałek jest kawałkiem górnym: podwójne zwolnienie lub uszkodzenie (top)

  • Jeśli następny kawałek znajduje się poza granicami areny: podwójne zwolnienie lub uszkodzenie (out)

  • Jeśli kawałek nie jest oznaczony jako używany (w prev_inuse z następnego kawałka): podwójne zwolnienie lub uszkodzenie (!prev)

  • Jeśli następny kawałek ma zbyt mały lub zbyt duży rozmiar: free(): nieprawidłowy następny rozmiar (normalny)

  • jeśli poprzedni kawałek nie jest używany, spróbuje go skonsolidować. Ale jeśli prev_size różni się od rozmiaru wskazanego w poprzednim kawałku: uszkodzony rozmiar vs. prev_size podczas konsolidacji

Kod _int_free_merge_chunk

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

Ucz się i praktykuj Hacking AWS: <img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
Ucz się i praktykuj Hacking GCP: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)

<details>

<summary>Wesprzyj HackTricks</summary>

* Sprawdź [**plany subskrypcyjne**](https://github.com/sponsors/carlospolop)!
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Udostępniaj sztuczki hakerskie, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) na githubie.

</details>

</div>

Last updated