12 KiB
Heap
Osnove Heap-a
Heap je osnovno mesto gde program može skladištiti podatke kada zahteva podatke pozivajući funkcije poput malloc
, calloc
... Osim toga, kada ti podaci više nisu potrebni, oni se oslobađaju pozivom funkcije free
.
Kao što je prikazano, heap se nalazi odmah nakon što se binarni fajl učita u memoriju (proverite [heap]
sekciju):
Osnovna Aloakcija Blokova
Kada se zahteva da se neki podatak skladišti u heap-u, određeni prostor heap-a se alocira za to. Taj prostor će pripadati binu i rezervisaće se samo za traženi podatak + prostor za zaglavlja binova + minimalno pomeranje veličine binova za blok. Cilj je rezervisati što manje memorije bez komplikovanja pronalaženja svakog bloka. Za to se koriste informacije o metapodacima bloka kako bi se znalo gde se nalazi svaki korišćeni/slobodan blok.
Postoje različiti načini rezervisanja prostora, uglavnom zavisno o korišćenom binu, ali opšta metodologija je sledeća:
- Program počinje sa zahtevanjem određene količine memorije.
- Ako u listi blokova postoji dovoljno veliki blok koji može ispuniti zahtev, on će biti korišćen.
- To može značiti da će deo dostupnog bloka biti korišćen za ovaj zahtev, a ostatak će biti dodat u listu blokova.
- Ako nema dostupnog bloka u listi, ali još uvek postoji prostor u alociranoj memoriji heap-a, menadžer heap-a kreira novi blok.
- Ako nema dovoljno prostora u heap-u da se alocira novi blok, menadžer heap-a traži od kernela da proširi memoriju alociranu za heap, a zatim koristi tu memoriju da generiše novi blok.
- Ako sve propadne,
malloc
vraća null.
Imajte na umu da ako tražena memorija pređe prag, mmap
će se koristiti za mapiranje tražene memorije.
Arene
U višenitnim aplikacijama, menadžer heap-a mora sprečiti trke za stanjem koje bi mogle dovesti do rušenja. Početno je to bilo postignuto korišćenjem globalne međusobne isključivosti kako bi se osiguralo da samo jedna nit može pristupiti heap-u u isto vreme, ali to je uzrokovalo probleme sa performansama zbog uskog grla koje je prouzrokovala međusobna isključivost.
Da bi se to rešilo, ptmalloc2 allocator heap-a je uveo "arene", gde svaka arena deluje kao zaseban heap sa svojim sopstvenim podacima strukture i međusobnom isključivošću, omogućavajući više niti da obavljaju operacije heap-a bez mešanja, pod uslovom da koriste različite arene.
Podrazumevana "glavna" arena upravlja operacijama heap-a za aplikacije sa jednom niti. Kada se dodaju nove niti, menadžer heap-a im dodeljuje sekundarne arene kako bi smanjio sukob. Prvo pokušava da svaku novu nit poveže sa neiskorišćenom arenom, kreirajući nove ako je potrebno, do granice od 2 puta broja CPU jezgara za 32-bitne sisteme i 8 puta za 64-bitne sisteme. Kada se dostigne granica, niti moraju deliti arene, što može dovesti do potencijalnih sukoba.
Za razliku od glavne arene, koja se proširuje korišćenjem brk
sistemskog poziva, sekundarne arene kreiraju "pod-heapove" korišćenjem mmap
i mprotect
kako bi simulirale ponašanje heap-a, omogućavajući fleksibilnost u upravljanju memorijom za višenitne operacije.
Pod-heapovi
Pod-heapovi služe kao rezerve memorije za sekundarne arene u višenitnim aplikacijama, omogućavajući im da rastu i upravljaju svojim sopstvenim regionima heap-a odvojeno od glavnog heap-a. Evo kako se pod-heapovi razlikuju od početnog heap-a i kako funkcionišu:
- Početni Heap vs. Pod-heapovi:
- Početni heap se nalazi odmah nakon binarnog fajla programa u memoriji, i proširuje se korišćenjem
sbrk
sistemskog poziva. - Pod-heapovi, korišćeni od strane sekundarnih arena, kreiraju se kroz
mmap
, sistemski poziv koji mapira određeni memorijski region.
- Rezervacija Memorije sa
mmap
:
- Kada menadžer heap-a kreira pod-heap, rezerviše veliki blok memorije kroz
mmap
. Ova rezervacija ne alocira memoriju odmah; jednostavno označava region koji druge sistemskih procesi ili alokacije ne bi trebali koristiti. - Podrazumevana veličina rezervacije za pod-heap je 1 MB za 32-bitne procese i 64 MB za 64-bitne procese.
- Postepeno Proširenje sa
mprotect
:
- Rezervisani memorijski region je prvobitno označen kao
PROT_NONE
, što znači da kernel ne mora alocirati fizičku memoriju za ovaj prostor još. - Da bi "proširio" pod-heap, menadžer heap-a koristi
mprotect
da promeni dozvole stranica saPROT_NONE
naPROT_READ | PROT_WRITE
, što podstiče kernel da alocira fizičku memoriju na prethodno rezervisane adrese. Ovaj postepeni pristup omogućava pod-heapu da se proširi po potrebi. - Kada se ceo pod-heap iscrpi, menadžer heap-a kreira novi pod-heap da nastavi sa alokacijom.
malloc_state
Svaki heap (glavna arena ili arene drugih niti) ima malloc_state
strukturu.
Važno je primetiti da je malloc_state
struktura glavne arene globalna promenljiva u libc-u (stoga se nalazi u prostoru memorije libc-a).
U slučaju malloc_state
struktura heap-ova niti, one se nalaze unutar sopstvenog "heap-a" niti.
Postoje neke zanimljive stvari koje treba primetiti iz ove strukture (videti C kod ispod):
mchunkptr bins[NBINS * 2 - 2];
sadrži pokazivače na prvi i poslednji blokove malih, velikih i nesortiranih binova (-2 je zato što se indeks 0 ne koristi)- Stoga, prvi blok ovih binova će imati pokazivač unazad na ovu strukturu i poslednji blok ovih binova će imati pokazivač unapred na ovu strukturu. Što u osnovi znači da ako možete procureti ove adrese u glavnoj areni imaćete pokazivač na strukturu u libc-u.
- Strukture
struct malloc_state *next;
istruct malloc_state *next_free;
su povezane liste arena - Blok
top
je poslednji "blok", koji je u osnovi sav preostali prostor heap-a. Kada je top blok "prazan", heap je potpuno iskorišćen i potrebno je zatražiti više prostora. - Blok
last reminder
dolazi iz slučajeva kada nije dostupan tačno određen blok veličine i stoga se veći blok deli, a preostali deo pokazivača se smešta ovde.
// From https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/malloc_state
struct malloc_state
{
/* Serialize access. */
__libc_lock_define (, mutex);
/* Flags (formerly in max_fast). */
int flags;
/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];
/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;
/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;
/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];
/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];
/* Linked list */
struct malloc_state *next;
/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;
/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
INTERNAL_SIZE_T attached_threads;
/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};
typedef struct malloc_state *mstate;
malloc_chunk
Ova struktura predstavlja određeni deo memorije. Različita polja imaju različito značenje za alocirane i nealocirane delove.
// From https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/malloc_chunk
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk, if it is free. */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if this chunk is free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if this chunk is free. */
struct malloc_chunk* bk_nextsize;
};
typedef struct malloc_chunk* mchunkptr;
Kao što je ranije komentarisano, ovi delovi takođe imaju neke metapodatke, veoma dobro predstavljene na ovoj slici:
Metapodaci obično su 0x08B što ukazuje na trenutnu veličinu dela koristeći poslednja 3 bita da označe:
A
: Ako je 1, dolazi iz pod-heapa, ako je 0, nalazi se u glavnoj areniM
: Ako je 1, ovaj deo je deo prostora alociranog sa mmap i nije deo heap-aP
: Ako je 1, prethodni deo je u upotrebi
Zatim, prostor za korisničke podatke, i na kraju 0x08B da označi veličinu prethodnog dela kada je deo dostupan (ili za skladištenje korisničkih podataka kada je alociran).
Osim toga, kada su dostupni, korisnički podaci se koriste i za sadržavanje nekih podataka:
- Pokazivač na sledeći deo
- Pokazivač na prethodni deo
- Veličina sledećeg dela na listi
- Veličina prethodnog dela na listi
{% hint style="info" %} Primetite kako organizovanje liste na ovaj način sprečava potrebu za imanjem niza gde je svaki pojedinačni deo registrovan. {% endhint %}
Brzi primer Heap-a
Brzi primer heap-a sa https://guyinatuxedo.github.io/25-heap/index.html ali u arm64:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(void)
{
char *ptr;
ptr = malloc(0x10);
strcpy(ptr, "panda");
}
Postavite prekidnu tačku na kraju glavne funkcije i saznajmo gde je informacija sačuvana:
Moguće je videti da je string panda sačuvan na 0xaaaaaaac12a0
(što je adresa data kao odgovor od strane malloc unutar x0
). Proveravajući 0x10 bajtova pre toga, moguće je videti da 0x0
predstavlja da prethodni blok nije korišćen (dužina 0) i da je dužina ovog bloka 0x21
.
Dodatni prostor rezervisan (0x21-0x10=0x11) dolazi od dodatnih zaglavlja (0x10) i 0x1 ne znači da je rezervisano 0x21B već poslednja 3 bita dužine trenutnog zaglavlja imaju neka posebna značenja. Pošto je dužina uvek poravnata na granicu od 16 bajtova (na 64-bitnim mašinama), ovi bitovi zapravo nikada neće biti korišćeni od strane broja dužine.
0x1: Previous in Use - Specifies that the chunk before it in memory is in use
0x2: Is MMAPPED - Specifies that the chunk was obtained with mmap()
0x4: Non Main Arena - Specifies that the chunk was obtained from outside of the main arena
Bins & Aloakcije/Memorija
Proverite šta su to binovi, kako su organizovani i kako se memorija alocira i oslobađa u:
{% content-ref url="bins-and-memory-allocations.md" %} bins-and-memory-allocations.md {% endcontent-ref %}
Provere Bezbednosti Funkcija Heap-a
Funkcije uključene u heap će izvršiti određene provere pre nego što obave svoje akcije kako bi se osiguralo da heap nije oštećen:
{% content-ref url="heap-functions-security-checks.md" %} heap-functions-security-checks.md {% endcontent-ref %}