free
Last updated
Last updated
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Proverite planove pretplate!
Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
(Nema provera objašnjenih u ovom sažetku i neki slučajevi su izostavljeni radi sažetosti)
Ako je adresa null, ne radite ništa
Ako je deo bio mmapovan, mummapujte ga i završite
Pozovite _int_free
:
Ako je moguće, dodajte deo u tcache
Ako je moguće, dodajte deo u fast bin
Pozovite _int_free_merge_chunk
da konsolidujete deo ako je potrebno i dodajte ga u nesortiranu listu
Free
poziva __libc_free
.
Ako je adresa koja je prosleđena Null (0), ne radite ništa.
Proverite oznaku pokazivača
Ako je deo mmapovan
, mummap
ujte ga i to je to
Ako nije, dodajte boju i pozovite _int_free
nad njim
```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>
Počinje sa nekim proverama koje osiguravaju:
* da je **pokazivač** **usklađen,** ili pokreće grešku `free(): invalid pointer`
* da **veličina** nije manja od minimuma i da je **veličina** takođe **usklađena** ili pokreće grešku: `free(): invalid size`
<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);
Prvo će pokušati da alocira ovaj deo u povezanoj tcache. Međutim, prethodno se vrše neka proveravanja. Proći će kroz sve delove tcache na istom indeksu kao oslobođeni deo i:
Ako ima više unosa nego mp_.tcache_count
: free(): previše delova otkriveno u tcache
Ako unos nije poravnat: free(): otkriven neporavnat deo u tcache 2
ako je oslobođeni deo već bio oslobođen i prisutan je kao deo u tcache: free(): otkriveno duplo oslobađanje u tcache 2
Ako sve prođe dobro, deo se dodaje u tcache i funkcija se vraća.
```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>
Počnite proverom da li je veličina pogodna za fast bin i proverite da li je moguće postaviti je blizu top chunk-a.
Zatim, dodajte oslobođeni chunk na vrh fast bin-a dok vršite neke provere:
* Ako je veličina chunk-a nevalidna (prevelika ili premala) aktivirajte: `free(): invalid next size (fast)`
* Ako je dodatni chunk već bio vrh fast bin-a: `double free or corruption (fasttop)`
* Ako veličina chunk-a na vrhu ima drugačiju veličinu od chunk-a koji dodajemo: `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)");
}
Ako deo još nije dodeljen nijednom kontejneru, pozovite _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); } }
</details>
## \_int\_free\_merge\_chunk
Ova funkcija će pokušati da spoji chunk P od SIZE bajtova sa svojim susedima. Stavite rezultantni chunk na listu nesortiranih binova.
Neki provere se vrše:
* Ako je chunk gornji chunk: `double free or corruption (top)`
* Ako je sledeći chunk van granica arene: `double free or corruption (out)`
* Ako chunk nije označen kao korišćen (u `prev_inuse` sledećeg chucka): `double free or corruption (!prev)`
* Ako sledeći chunk ima premalu ili preveliku veličinu: `free(): invalid next size (normal)`
* ako prethodni chunk nije u upotrebi, pokušaće da konsoliduje. Ali, ako se prev\_size razlikuje od veličine navedene u prethodnom chunku: `corrupted size vs. prev_size while consolidating`
<details>
<summary>_int_free_merge_chunk code</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);
}
Proverite planove pretplate!
Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)