malloc & sysmalloc

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Riepilogo dell'Ordine di Allocazione

(Nessun controllo è spiegato in questo riepilogo e alcuni casi sono stati omessi per brevità)

  1. __libc_malloc cerca di ottenere un chunk dal tcache, se non riesce chiama _int_malloc

  2. _int_malloc :

  3. Cerca di generare l'arena se non ce n'è una

  4. Se c'è un chunk di fast bin della dimensione corretta, usalo

  5. Riempie il tcache con altri chunk veloci

  6. Se c'è un chunk di small bin della dimensione corretta, usalo

  7. Riempie il tcache con altri chunk di quella dimensione

  8. Se la dimensione richiesta non è per small bins, consolida il fast bin in unsorted bin

  9. Controlla l'unsorted bin, usa il primo chunk con spazio sufficiente

  10. Se il chunk trovato è più grande, dividilo per restituire una parte e aggiungi il resto all'unsorted bin

  11. Se un chunk è della stessa dimensione della dimensione richiesta, usalo per riempire il tcache invece di restituirlo (fino a quando il tcache è pieno, poi restituisci il successivo)

  12. Per ogni chunk di dimensione più piccola controllato, mettilo nel suo rispettivo small o large bin

  13. Controlla il large bin nell'indice della dimensione richiesta

  14. Inizia a cercare dal primo chunk che è più grande della dimensione richiesta, se ne trovi uno restituiscilo e aggiungi i resti al small bin

  15. Controlla i large bins dagli indici successivi fino alla fine

  16. Dal prossimo indice più grande controlla se ci sono chunk, dividi il primo chunk trovato per usarlo per la dimensione richiesta e aggiungi il resto all'unsorted bin

  17. Se non viene trovato nulla nei bins precedenti, prendi un chunk dal top chunk

  18. Se il top chunk non era abbastanza grande, ingrandiscilo con sysmalloc

__libc_malloc

La funzione malloc chiama effettivamente __libc_malloc. Questa funzione controllerà il tcache per vedere se c'è un chunk disponibile della dimensione desiderata. Se c'è, lo utilizzerà e se non c'è controllerà se è un singolo thread e in tal caso chiamerà _int_malloc nell'arena principale, e se non lo è chiamerà _int_malloc nell'arena del thread.

__libc_malloc codice

```c // From https://github.com/bminor/glibc/blob/master/malloc/malloc.c

#if IS_IN (libc) void * __libc_malloc (size_t bytes) { mstate ar_ptr; void *victim;

_Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2, "PTRDIFF_MAX is not more than half of SIZE_MAX");

if (!__malloc_initialized) ptmalloc_init (); #if USE_TCACHE /* int_free also calls request2size, be careful to not pad twice. */ size_t tbytes = checked_request2size (bytes); if (tbytes == 0) { __set_errno (ENOMEM); return NULL; } size_t tc_idx = csize2tidx (tbytes);

MAYBE_INIT_TCACHE ();

DIAG_PUSH_NEEDS_COMMENT; if (tc_idx < mp_.tcache_bins && tcache != NULL && tcache->counts[tc_idx] > 0) { victim = tcache_get (tc_idx); return tag_new_usable (victim); } DIAG_POP_NEEDS_COMMENT; #endif

if (SINGLE_THREAD_P) { victim = tag_new_usable (_int_malloc (&main_arena, bytes)); assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || &main_arena == arena_for_chunk (mem2chunk (victim))); return victim; }

arena_get (ar_ptr, bytes);

victim = _int_malloc (ar_ptr, bytes); /* Retry with another arena only if we were able to find a usable arena before. */ if (!victim && ar_ptr != NULL) { LIBC_PROBE (memory_malloc_retry, 1, bytes); ar_ptr = arena_get_retry (ar_ptr, bytes); victim = _int_malloc (ar_ptr, bytes); }

if (ar_ptr != NULL) __libc_lock_unlock (ar_ptr->mutex);

victim = tag_new_usable (victim);

assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || ar_ptr == arena_for_chunk (mem2chunk (victim))); return victim; }

</details>

Nota come etichetterà sempre il puntatore restituito con `tag_new_usable`, dal codice:
```c
void *tag_new_usable (void *ptr)

Allocate a new random color and use it to color the user region of
a chunk; this may include data from the subsequent chunk's header
if tagging is sufficiently fine grained.  Returns PTR suitably
recolored for accessing the memory there.

_int_malloc

Questa è la funzione che alloca memoria utilizzando gli altri bin e il top chunk.

  • Inizio

Inizia definendo alcune variabili e ottenendo la dimensione reale che lo spazio di memoria richiesto deve avere:

Fast Bin

Se la dimensione necessaria è all'interno delle dimensioni dei Fast Bins, prova a utilizzare un chunk dal fast bin. Fondamentalmente, in base alla dimensione, troverà l'indice del fast bin dove dovrebbero trovarsi i chunk validi e, se presenti, restituirà uno di essi. Inoltre, se tcache è abilitato, riempirà il tcache bin di quella dimensione con i fast bins.

Durante l'esecuzione di queste azioni, vengono eseguiti alcuni controlli di sicurezza:

  • Se il chunk è disallineato: malloc(): unaligned fastbin chunk detected 2

  • Se il chunk successivo è disallineato: malloc(): unaligned fastbin chunk detected

  • Se il chunk restituito ha una dimensione che non è corretta a causa del suo indice nel fast bin: malloc(): memory corruption (fast)

  • Se un chunk utilizzato per riempire il tcache è disallineato: malloc(): unaligned fastbin chunk detected 3

malloc_consolidate

Se non era un piccolo blocco, è un grande blocco, e in questo caso malloc_consolidate viene chiamato per evitare la frammentazione della memoria.

Unsorted bin

È tempo di controllare l'unsorted bin per un potenziale chunk valido da utilizzare.

Inizio

Questo inizia con un grande ciclo for che attraverserà l'unsorted bin nella direzione bk fino ad arrivare alla fine (la struttura arena) con while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))

Inoltre, vengono eseguiti alcuni controlli di sicurezza ogni volta che un nuovo chunk viene considerato:

  • Se la dimensione del chunk è strana (troppo piccola o troppo grande): malloc(): invalid size (unsorted)

  • Se la dimensione del chunk successivo è strana (troppo piccola o troppo grande): malloc(): invalid next size (unsorted)

  • Se la dimensione precedente indicata dal chunk successivo differisce dalla dimensione del chunk: malloc(): mismatching next->prev_size (unsorted)

  • Se non victim->bck->fd == victim o non victim->fd == av (arena): malloc(): unsorted double linked list corrupted

  • Poiché stiamo sempre controllando l'ultimo, il suo fd dovrebbe sempre puntare alla struttura arena.

  • Se il chunk successivo non indica che il precedente è in uso: malloc(): invalid next->prev_inuse (unsorted)

Se questo ha avuto successo, restituisci il chunk e finisce qui, altrimenti continua a eseguire la funzione...

se dimensione uguale

Continua a rimuovere il chunk dal bin, nel caso in cui la dimensione richiesta sia esattamente quella del chunk:

  • Se il tcache non è pieno, aggiungilo al tcache e continua indicando che c'è un chunk tcache che potrebbe essere utilizzato

  • Se il tcache è pieno, usalo semplicemente restituendolo

_int_malloc limiti

A questo punto, se un chunk è stato memorizzato nel tcache che può essere utilizzato e il limite è stato raggiunto, basta restituire un chunk dal tcache.

Inoltre, se MAX_ITERS è stato raggiunto, esci dal ciclo e ottieni un chunk in un modo diverso (top chunk).

Se return_cached è stato impostato, basta restituire un chunk dal tcache per evitare ricerche più ampie.

Se un chunk non è trovato adatto per questo, continua

Large Bin (prossimo più grande)

Se nel large bin esatto non c'era alcun chunk che potesse essere utilizzato, inizia a scorrere tutti i successivi large bin (partendo da quello immediatamente più grande) fino a trovarne uno (se presente).

Il resto del chunk diviso viene aggiunto nel bin non ordinato, last_reminder viene aggiornato e viene eseguita la stessa verifica di sicurezza:

  • bck->fd-> bk != bck: malloc(): corrupted unsorted chunks2

sysmalloc

sysmalloc inizio

Se l'arena è nulla o la dimensione richiesta è troppo grande (e ci sono mmaps permessi rimasti) usa sysmalloc_mmap per allocare spazio e restituirlo.

sysmalloc non main arena

Prima cercherà di estendere l'heap precedente per questo heap. Se non è possibile, cercherà di allocare un nuovo heap e aggiornare i puntatori per poterlo utilizzare. Infine, se non ha funzionato, proverà a chiamare sysmalloc_mmap.

sysmalloc main arena previous error 1

Se il precedente ha restituito MORECORE_FAILURE, prova di nuovo ad allocare memoria usando sysmalloc_mmap_fallback

sysmalloc finale

Completa l'allocazione aggiornando le informazioni dell'arena.

// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L2921C3-L2943C12

if ((unsigned long) av->system_mem > (unsigned long) (av->max_system_mem))
av->max_system_mem = av->system_mem;
check_malloc_state (av);

/* finally, do the allocation */
p = av->top;
size = chunksize (p);

/* check that one of the above allocation paths succeeded */
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
{
remainder_size = size - nb;
remainder = chunk_at_offset (p, nb);
av->top = remainder;
set_head (p, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
check_malloced_chunk (av, p, nb);
return chunk2mem (p);
}

/* catch all failure paths */
__set_errno (ENOMEM);
return 0;

sysmalloc_mmap

Last updated