free

Podržite HackTricks

Besplatni Pregled Narudžbine

(Nisu objašnjene sve provere u ovom pregledu i neki slučajevi su izostavljeni radi sažetosti)

  1. Ako je adresa null, ne čini ništa

  2. Ako je blok bio mapiran, odmapiraj ga i završi

  3. Pozovi _int_free:

  4. Ako je moguće, dodaj blok u tcache

  5. Ako je moguće, dodaj blok u brzi bin

  6. Pozovi _int_free_merge_chunk da konsoliduje blok ako je potrebno i dodaj ga u nesortiranu listu

__libc_free

Free poziva __libc_free.

  • Ako je prosleđena adresa Null (0), ne čini ništa.

  • Proveri oznaku pokazivača

  • Ako je blok mapiran, odmapiraj ga i to je sve

  • Ako nije, dodaj boju i pozovi _int_free nad njim

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

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

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

Počinje sa nekim proverama kako bi se osiguralo da:

* **pokazivač** je **poravnan,** ili pokreće grešku `free(): invalid pointer`
* **veličina** nije manja od minimuma i da je **veličina** takođe **poravnata** ili pokreće grešku: `free(): invalid size`

<details>

<summary>Početak \_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

Prvo će pokušati da alocira ovaj blok u povezanoj tcache. Međutim, neke provere se vrše unapred. Proći će kroz sve blokove tcache-a na istom indeksu kao oslobođeni blok i:

  • Ako ima više unosa od mp_.tcache_count: free(): previše blokova otkriveno u tcache-u

  • Ako unos nije poravnan: free(): otkriven neusaglašen blok u tcache-u 2

  • ako je oslobođeni blok već oslobođen i prisutan je kao blok u tcache-u: free(): otkriven dvostruki free u tcache-u 2

Ako sve prođe kako treba, blok se dodaje u tcache i funkcija se završava.

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

Počnite proverom da li je veličina pogodna za brzi bin i proverite da li je moguće postaviti je blizu vršnog bloka.

Zatim dodajte oslobođeni blok na vrh brzog bina dok vršite neke provere:

* Ako je veličina bloka nevažeća (previše velika ili mala) izazovite: `free(): invalid next size (fast)`
* Ako je dodati blok već bio na vrhu brzog bina: `double free or corruption (fasttop)`
* Ako veličina bloka na vrhu ima drugačiju veličinu od bloka koji dodajemo: `invalid fastbin entry (free)`

<details>

<summary>_int_free Brzi 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 završetak

Ako se blok još nije dodelio ni na jedan bin, pozovi _int_free_merge_chunk

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

Ova funkcija će pokušati da spoji blok P veličine SIZE bajtova sa svojim susedima. Rezultujući blok će biti stavljen na listu nesortiranih blokova.

Vrše se neke provere:

  • Ako je blok vrhunski blok: double free or corruption (top)

  • Ako je sledeći blok van granica arene: double free or corruption (out)

  • Ako blok nije označen kao korišćen (u prev_inuse od sledećeg bloka): double free or corruption (!prev)

  • Ako sledeći blok ima previše malu ili preveliku veličinu: free(): invalid next size (normal)

  • ako prethodni blok nije u upotrebi, pokušaće da konsoliduje. Ali, ako se prev_size razlikuje od veličine naznačene u prethodnom bloku: corrupted size vs. prev_size while consolidating

_int_free_merge_chunk kod

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

Učite i vežbajte hakovanje AWS-a: <img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks obuka AWS Crveni Tim Ekspert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
Učite i vežbajte hakovanje GCP-a: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks obuka GCP Crveni Tim Ekspert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)

<details>

<summary>Podržite HackTricks</summary>

* Proverite [**planove pretplate**](https://github.com/sponsors/carlospolop)!
* **Pridružite se** 💬 [**Discord grupi**](https://discord.gg/hRep4RUj7f) ili [**telegram grupi**](https://t.me/peass) ili nas **pratite** na **Twitteru** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Podelite hakovanje trikova slanjem PR-ova na** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repozitorijume.

</details>

</div>

Last updated