Heap, bir programın **`malloc`**, `calloc` gibi fonksiyonları çağırarak veri istediğinde verileri depolayabileceği yerdir. Ayrıca, bu belleğe artık ihtiyaç duyulmadığında **`free`** fonksiyonu çağrılarak serbest bırakılır.
Görüldüğü gibi, bu bellek, binary belleğe yüklendikten hemen sonra bulunmaktadır ( `[heap]` bölümüne bakınız):
Heap'e depolanacak veri istendiğinde, heap'in bir kısmı buna ayrılır. Bu alan bir bine ait olacak ve yalnızca istenen veri + bin başlıklarının alanı + minimum bin boyutu ofseti, parça için ayrılmış olacaktır. Amacı, her parçanın nerede olduğunu bulmayı karmaşık hale getirmeden mümkün olduğunca az bellek rezerve etmektir. Bunun için, kullanılan/boş parça bilgilerini bilmek için meta veri parça bilgileri kullanılır.
* Bu, isteğin bir kısmı için kullanılacak olan mevcut parçanın bir kısmının kullanılacağı ve geri kalanının parça listesine ekleneceği anlamına gelebilir.
* Liste içinde uygun bir parça yoksa ancak ayrılmış heap belleğinde hala yer varsa, heap yöneticisi yeni bir parça oluşturur.
* Yeni bir parça tahsis etmek için yeterli heap alanı yoksa, heap yöneticisi kernel'den heap'e ayrılan belleği genişletmesini ister ve ardından bu belleği kullanarak yeni parça oluşturur.
**Çoklu iş parçacıklı** uygulamalarda, heap yöneticisi, çökmelere yol açabilecek **yarış koşullarını** önlemelidir. Başlangıçta, bunu sadece bir iş parçacığının aynı anda heap'e erişebileceğinden emin olmak için bir **global kilitleme** kullanarak yapılıyordu, ancak bu, kilitleme nedeniyle performans sorunlarına yol açtı.
Bunu ele almak için, ptmalloc2 heap tahsis edicisi, her birinin **kendi** veri **yapıları** ve **kilidi** olan **ayrı bir heap** olarak hareket eden "arenaları" tanıttı, bu da farklı arenaları kullansalar bile birden fazla iş parçacığının birbirini engellemeden heap işlemleri yapmasına izin verir.
Varsayılan "ana" arena, tek iş parçacıklı uygulamalar için heap işlemlerini yönetir. **Yeni iş parçacıkları** eklendiğinde, heap yöneticisi çekişmeyi azaltmak için onlara **ikincil arenalar** atar. Her yeni iş parçacığını kullanılmayan bir arenaya eklemeye çalışır, gerektiğinde yeni arenalar oluşturur, 32 bit sistemler için CPU çekirdekleri için 2 kat, 64 bit sistemler için 8 kat sınırına ulaşana kadar. Sınır aşıldığında, **iş parçacıkları arenaları paylaşmak zorunda kalır**, potansiyel çekişmeye yol açar.
Ana arenanın aksine, `brk` sistem çağrısını kullanarak genişleyen ana arenalar, çoklu iş parçacıklı işlemler için belleği yönetme esnekliği sağlayan `mmap` ve `mprotect` kullanarak "alt heap"ler oluşturan ikincil arenalar oluşturur.
Alt heap'ler, çoklu iş parçacıklı uygulamalardaki ikincil arenalar için bellek rezervleri olarak hizmet eder, kendi heap bölgelerini ana heap'ten ayrı olarak büyütmelerine ve yönetmelerine olanak tanır. İşte alt heap'lerin başlangıç heap'inden nasıl farklı olduğu ve nasıl çalıştığı:
1.**Başlangıç Heap'i vs. Alt Heap'ler**:
* Başlangıç heap'i, programın binary'sinin hemen ardında bulunur ve `sbrk` sistem çağrısını kullanarak genişler.
* İkincil arenalar tarafından kullanılan alt heap'ler, belirli bir bellek bölgesini eşleyen `mmap` kullanılarak oluşturulur.
2.**`mmap` ile Bellek Rezervasyonu**:
* Heap yöneticisi bir alt heap oluşturduğunda, `mmap` aracılığıyla büyük bir bellek bloğu rezerve eder. Bu rezervasyon hemen bellek tahsis etmez; sadece diğer sistem işlemlerinin veya tahsislerin kullanmaması gereken bir bölgeyi belirler.
* Rezerve edilen bellek bölgesi başlangıçta `PROT_NONE` olarak işaretlenir, bu da kernelin bu alana henüz fiziksel bellek tahsis etmesine gerek olmadığını gösterir.
* Alt heap'i "genişletmek" için heap yöneticisi, `mprotect` kullanarak sayfa izinlerini `PROT_NONE`dan `PROT_READ | PROT_WRITE`'a değiştirir, bu da kernelin önceden rezerve edilen adreslere fiziksel bellek tahsis etmesini sağlar. Bu adım adım yaklaşım, alt heap'in ihtiyaç duyuldukça genişlemesine olanak tanır.
* Tüm alt heap tükenene kadar, heap yöneticisi yeni bir alt heap oluşturarak tahsis işlemine devam eder.
Her heap'in (ana arena veya diğer iş parçacıklarının arenaları) bir **`malloc_state` yapısı** vardır.\
Önemli bir nokta, **ana arenanın `malloc_state` yapısının** libc'de **global bir değişken** olduğudur (bu nedenle libc bellek alanında bulunur).\
İş parçacıklarının arenalarının `malloc_state` yapıları ise **kendi iş parçacığı "heap"lerinin içinde** bulunur.
Bu yapıdan bazı ilginç noktaları not etmek önemlidir (aşağıdaki C koduna bakınız):
*`mchunkptr bins[NBINS * 2 - 2];`, küçük, büyük ve sıralanmamış **binlerin ilk ve son parçalarına işaretçiler** içerir (-2, çünkü indeks 0 kullanılmaz)
* Dolayısıyla, bu binlerin **ilk parçaları** bu yapıya **ters işaretçiye sahip olacak** ve bu binlerin **son parçaları** bu yapıya **ileri işaretçiye sahip olacaktır**. Bu temelde, eğer bu adresleri **ana arenada sızdırabilirseniz**, **libc** içindeki yapıya bir işaretçiye sahip olacaksınız.
*`struct malloc_state *next;` ve `struct malloc_state *next_free;` yapılarının arenaların bağlı listeleri olduğu
*`top` parça, temelde **tüm heap hatırlama alanı olan** son "parçadır". Top parça "boş" olduğunda, heap tamamen kullanılmıştır ve daha fazla alan istenmesi gerekir.
*`last reminder` parça, tam bir boyutta parça mevcut olmadığı durumlardan kaynaklanan, daha büyük bir parça bölündüğünde, geriye kalan kısmın yerleştirildiği bir noktadır.
```c
// 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
Bu yapı belirli bir bellek parçasını temsil eder. Çeşitli alanlar ayrılmış ve ayrılmamış parçalar için farklı anlamlara sahiptir.
```c
// 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;
```
Daha önce belirtildiği gibi, bu parçaların bazı meta verileri de bulunmaktadır, bu meta veriler bu resimde çok iyi temsil edilmiştir:
Ardından, kullanıcı verileri için alan ve son olarak parça kullanımda değilken (veya ayrıldığında kullanıcı verilerini saklamak için) önceki parça boyutunu belirtmek için 0x08B bulunur.
Görülebileceği gibi, dize panda `0xaaaaaaac12a0` adresinde saklandı (bu adres, `x0` içindeki malloc tarafından yanıt olarak verilen adresdi). Onun 0x10 byte öncesini kontrol etmek mümkün olduğunda, `0x0`'ın **önceki parçanın kullanılmadığını** (uzunluğu 0) ve bu parçanın uzunluğunun `0x21` olduğunu görmek mümkündür.
Ekstra boşluklar ayrılmıştır (0x21-0x10=0x11) **eklenen başlıklardan** (0x10) gelir ve 0x1, 0x21B ayrılmış olduğu anlamına gelmez ancak mevcut başlığın uzunluğunun son 3 bitinin bazı özel anlamlara sahip olduğu anlamına gelir. Uzunluk her zaman 16 bayt hizalı olduğundan (64 bit makinelerde), bu bitler aslında uzunluk numarası tarafından asla kullanılmayacak.