Chunk'ların nasıl depolandığını daha verimli hale getirmek için her chunk sadece bir bağlı liste içinde değildir, birkaç tür vardır. Bunlar binlerdir ve 5 tür bin vardır: 62 küçük binler, 63 büyük binler, 1 sıralanmamış bin, 10 hızlı bin ve her bir iş parçacığı için 64 tcache bin.
Her sıralanmamış, küçük ve büyük bin için başlangıç adresi aynı dizinin içindedir. 0. indeks kullanılmaz, 1 sıralanmamış bindir, 2-64 arası küçük binlerdir ve 65-127 arası büyük binlerdir.
Tcache (İş Parçacığı Başına Önbellek) Binleri
İş parçacıkları kendi heap'lerine sahip olmaya çalışsalar da (bkz. Arenas ve Subheaps), çok sayıda iş parçacığına sahip bir sürecin (örneğin bir web sunucusu) başka iş parçacıklarıyla heap'i paylaşma olasılığı vardır. Bu durumda, ana çözüm kilitlerin kullanılmasıdır, bu da iş parçacıklarını önemli ölçüde yavaşlatabilir.
Bir iş parçacığı bir chunk'ı serbest bıraktığında, eğer tcache'de tahsis edilemeyecek kadar büyük değilse ve ilgili tcache bin dolu değilse (zaten 7 chunk), orada tahsis edilecektir. Eğer tcache'e giremiyorsa, serbest bırakma işlemini küresel olarak gerçekleştirebilmek için heap kilidini beklemesi gerekecektir.
Bir chunk tahsis edildiğinde, eğer Tcache'de gerekli boyutta serbest bir chunk varsa, onu kullanacaktır, yoksa, küresel binlerde bir tane bulabilmek veya yenisini oluşturabilmek için heap kilidini beklemesi gerekecektir.
Ayrıca bir optimizasyon vardır, bu durumda, heap kilidi varken, iş parçacığı istenen boyuttaki heap chunk'ları (7) ile Tcache'ini dolduracaktır, böylece daha fazlasına ihtiyaç duyarsa, Tcache'de bulacaktır.
Add a tcache chunk example
```c #include #include
int main(void) { char *chunk; chunk = malloc(24); printf("Address of the chunk: %p\n", (void *)chunk); gets(chunk); free(chunk); return 0; }
Derleyin ve ana fonksiyondaki ret opcode'unda bir kesme noktası ile hata ayıklayın. Ardından gef ile kullanılan tcache bin'ini görebilirsiniz:
```bash
gef➤ heap bins
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
Tcachebins[idx=0, size=0x20, count=1] ← Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
Tcache Yapıları ve Fonksiyonları
Aşağıdaki kodda max bins ve chunks per index'i, çift serbest bırakmaları önlemek için oluşturulan tcache_entry yapısını ve her bir thread'in bin'in her indeksine ait adresleri saklamak için kullandığı tcache_perthread_struct yapısını görmek mümkündür.
tcache_entry ve tcache_perthread_struct
```c // From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c
/* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */
/* With rounding and alignment, the bins are... idx 0 bytes 0..24 (64-bit) or 0..12 (32-bit) idx 1 bytes 25..40 or 13..20 idx 2 bytes 41..56 or 21..28 etc. */
/* This is another arbitrary limit, which tunables can change. Each tcache bin will hold at most this number of chunks. */
define TCACHE_FILL_COUNT 7
/* Maximum chunks in tcache bins for tunables. This value must fit the range of tcache->counts[] entries, else they may overflow. */
define MAX_TCACHE_COUNT UINT16_MAX
[...]
typedef struct tcache_entry { struct tcache_entry next; / This field exists to detect double frees. */ uintptr_t key; } tcache_entry;
/* There is one of these for each thread, which contains the per-thread cache (hence "tcache_perthread_struct"). Keeping overall size low is mildly important. Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons. */ typedef struct tcache_perthread_struct { uint16_t counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;
</details>
`__tcache_init` fonksiyonu, `tcache_perthread_struct` objesi için alan oluşturan ve tahsis eden fonksiyondur.
<details>
<summary>tcache_init kodu</summary>
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L3241C1-L3274C2
static void
tcache_init(void)
{
mstate ar_ptr;
void *victim = 0;
const size_t bytes = sizeof (tcache_perthread_struct);
if (tcache_shutting_down)
return;
arena_get (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
if (!victim && ar_ptr != NULL)
{
ar_ptr = arena_get_retry (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
}
if (ar_ptr != NULL)
__libc_lock_unlock (ar_ptr->mutex);
/* In a low memory situation, we may not be able to allocate memory
- in which case, we just keep trying later. However, we
typically do this very early, so either there is sufficient
memory, or there isn't enough memory to do non-trivial
allocations anyway. */
if (victim)
{
tcache = (tcache_perthread_struct *) victim;
memset (tcache, 0, sizeof (tcache_perthread_struct));
}
}
Tcache İndeksleri
Tcache, boyuta bağlı olarak birkaç bin içerir ve her indeksin ilk parçasına ve indeks başına parça sayısına işaret eden başlangıç işaretçileri bir parça içinde bulunur. Bu, bu bilgiyi (genellikle ilk olanı) içeren parçayı bulmanın, tüm tcache başlangıç noktalarını ve Tcache parçalarının sayısını bulmayı mümkün kıldığı anlamına gelir.
Hızlı Bins
Hızlı bins, küçük parçalar için bellek tahsisini hızlandırmak amacıyla, yakın zamanda serbest bırakılan parçaları hızlı erişim yapısında tutmak için tasarlanmıştır. Bu binler, Son Giren İlk Çıkar (LIFO) yaklaşımını kullanır, bu da en son serbest bırakılan parçanın yeni bir tahsis talebi olduğunda ilk olarak yeniden kullanılacağı anlamına gelir. Bu davranış hız açısından avantajlıdır, çünkü bir yığının (LIFO) üstünden eklemek ve çıkarmak, bir kuyruğa (FIFO) göre daha hızlıdır.
Ayrıca, hızlı bins tek bağlı listeler kullanır, çift bağlı değil, bu da hızı daha da artırır. Hızlı bins'teki parçalar komşularıyla birleştirilmediğinden, ortadan çıkarılmasına izin veren karmaşık bir yapıya ihtiyaç yoktur. Tek bağlı liste, bu işlemler için daha basit ve hızlıdır.
Temelde burada olan, başlığın (kontrol edilecek ilk parçaya işaretçi) her zaman o boyuttaki en son serbest bırakılan parçaya işaret etmesidir. Yani:
O boyutta yeni bir parça tahsis edildiğinde, başlık kullanılacak bir serbest parçaya işaret eder. Bu serbest parça, kullanılacak bir sonraki parçaya işaret ettiğinden, bu adres başlıkta saklanır, böylece bir sonraki tahsis nereden mevcut bir parça alacağını bilir.
Bir parça serbest bırakıldığında, serbest parça mevcut olan parçanın adresini saklayacak ve bu yeni serbest bırakılan parçanın adresi başlığa konulacaktır.
Bağlı listenin maksimum boyutu 0x80'dir ve 0x20 boyutundaki bir parça 0 indeksinde, 0x30 boyutundaki bir parça 1 indeksinde olacak şekilde düzenlenmiştir...
Hızlı bins'teki parçalar mevcut olarak ayarlanmamıştır, bu nedenle çevresindeki diğer serbest parçalarla birleştirilmek yerine bir süre hızlı bin parçaları olarak tutulurlar.
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711/*FastbinsAn array of lists holding recently freed small chunks. Fastbinsare not doubly linked. It is faster to single-link them, andsince chunks are never removed from the middles of these lists,double linking is not necessary. Also, unlike regular bins, theyare not even processed in FIFO order (they use faster LIFO) sinceordering doesn't much matter in the transient contexts in whichfastbins are normally used.Chunks in fastbins keep their inuse bit set, so they cannotbe consolidated with other free chunks. malloc_consolidatereleases all chunks in fastbins and consolidates them withother free chunks.*/typedefstruct malloc_chunk *mfastbinptr;#definefastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx])/* offset 2 to use otherwise unindexable first 2 bins */#definefastbin_index(sz) \((((unsignedint) (sz)) >> (SIZE_SZ ==8?4:3)) -2)/* The maximum fastbin request size we support */#defineMAX_FAST_SIZE (80* SIZE_SZ /4)#defineNFASTBINS (fastbin_index (request2size (MAX_FAST_SIZE)) +1)
Hızlı bir parça örneği ekle
```c #include #include
int main(void) { char *chunks[8]; int i;
// Loop to allocate memory 8 times for (i = 0; i < 8; i++) { chunks[i] = malloc(24); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, "Memory allocation failed at iteration %d\n", i); return 1; } printf("Address of chunk %d: %p\n", i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
return 0; }
Not edin ki, aynı boyutta 8 parça tahsis edip serbest bırakıyoruz, böylece tcache'i dolduruyoruz ve sekizinci parça hızlı parçaya kaydediliyor.
Bunu derleyin ve `main` fonksiyonundaki `ret` opcode'unda bir kesme noktası ile hata ayıklayın. Ardından `gef` ile tcache kutusunun dolu olduğunu ve bir parçanın hızlı kutuda olduğunu görebilirsiniz:
```bash
gef➤ heap bins
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
Tcachebins[idx=0, size=0x20, count=7] ← Chunk(addr=0xaaaaaaac1770, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1750, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1730, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1710, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac16f0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac16d0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
───────────────────────────────────────────────────────────────────────── Fastbins for arena at 0xfffff7f90b00 ─────────────────────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x20] ← Chunk(addr=0xaaaaaaac1790, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
Fastbins[idx=1, size=0x30] 0x00
Sıralanmamış kutu
Sıralanmamış kutu, bellek tahsisini daha hızlı hale getirmek için yığın yöneticisi tarafından kullanılan bir önbellek'tir. İşte nasıl çalıştığı: Bir program bir parça serbest bıraktığında ve bu parça bir tcache veya hızlı kutuda tahsis edilemiyorsa ve üst parçayla çakışmıyorsa, yığın yöneticisi hemen onu belirli bir küçük veya büyük kutuya koymaz. Bunun yerine, önce komşu serbest parçalarla birleştirmeyi dener ve daha büyük bir serbest bellek bloğu oluşturur. Ardından, bu yeni parçayı "sıralanmamış kutu" olarak adlandırılan genel bir kutuya yerleştirir.
Bir program bellek istediğinde, yığın yöneticisi sıralanmamış kutuyu kontrol eder ve yeterli boyutta bir parça olup olmadığını görür. Eğer bulursa, hemen kullanır. Eğer sıralanmamış kutuda uygun bir parça bulamazsa, bu listedeki tüm parçaları boyutlarına göre küçük veya büyük kutularına taşır.
Daha büyük bir parça iki yarıya bölünürse ve geri kalan MINSIZE'den büyükse, sıralanmamış kutuya geri yerleştirileceğini unutmayın.
Yani, sıralanmamış kutu, yakın zamanda serbest bırakılan belleği hızlı bir şekilde yeniden kullanarak bellek tahsisini hızlandırmanın ve zaman alıcı arama ve birleştirme ihtiyaçlarını azaltmanın bir yoludur.
Farklı kategorilerdeki parçalar olsa bile, eğer mevcut bir parça başka bir mevcut parça ile çakışıyorsa (başlangıçta farklı kutulara ait olsalar bile), birleştirileceklerdir.
Sıralanmamış bir parça örneği ekle
```c #include #include
int main(void) { char *chunks[9]; int i;
// Loop to allocate memory 8 times for (i = 0; i < 9; i++) { chunks[i] = malloc(0x100); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, "Memory allocation failed at iteration %d\n", i); return 1; } printf("Address of chunk %d: %p\n", i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
return 0; }
Not edin ki, aynı boyutta 9 parça ayırıp serbest bırakıyoruz, böylece **tcache'i dolduruyoruz** ve sekizinci parça **fastbin için çok büyük olduğu için** sıralanmamış kutuda saklanıyor ve dokuzuncu parça serbest bırakılmadığı için dokuzuncu ve sekizinci **üst parçayla birleştirilmiyor**.
Bunu derleyin ve `main` fonksiyonundaki `ret` opcode'unda bir kesme noktası ile hata ayıklayın. Ardından `gef` ile tcache kutusunun dolu olduğunu ve bir parçanın sıralanmamış kutuda olduğunu görebilirsiniz:
```bash
gef➤ heap bins
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
Tcachebins[idx=15, size=0x110, count=7] ← Chunk(addr=0xaaaaaaac1d10, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1c00, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1af0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac19e0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac18d0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac17c0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac12a0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
───────────────────────────────────────────────────────────────────────── Fastbins for arena at 0xfffff7f90b00 ─────────────────────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
─────────────────────────────────────────────────────────────────────── Unsorted Bin for arena at 0xfffff7f90b00 ───────────────────────────────────────────────────────────────────────
[+] unsorted_bins[0]: fw=0xaaaaaaac1e10, bk=0xaaaaaaac1e10
→ Chunk(addr=0xaaaaaaac1e20, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in unsorted bin.
Küçük Bins
Küçük bins, büyük bins'lerden daha hızlıdır ancak hızlı bins'lerden daha yavaştır.
62 binin her biri aynı boyutta parçalar içerecektir: 16, 24, ... (32 bit'te maksimum boyut 504 byte ve 64 bit'te 1024 byte). Bu, bir alanın tahsis edilmesi gereken binin bulunmasında ve bu listelerdeki girişlerin eklenip kaldırılmasında hız sağlar.
Küçük binin boyutu, binin indeksine göre şu şekilde hesaplanır:
En küçük boyut: 2*4*indeks (örneğin, indeks 5 -> 40)
En büyük boyut: 2*8*indeks (örneğin, indeks 5 -> 80)
// Loop to allocate memory 8 times for (i = 0; i < 9; i++) { chunks[i] = malloc(0x100); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, "Memory allocation failed at iteration %d\n", i); return 1; } printf("Address of chunk %d: %p\n", i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
chunks[9] = malloc(0x110);
return 0; }
Not edin ki 9 adet aynı boyutta parça ayırıp serbest bıraktığımızda, bunlar **tcache'i doldurur** ve sekizinci parça **fastbin için çok büyük olduğu** için sıralanmamış kutuda saklanır ve dokuzuncu parça serbest bırakılmadığı için dokuzuncu ve sekizinci parçalar **üst parçayla birleştirilmez**. Sonra 0x110 boyutunda daha büyük bir parça ayırdığımızda, **sıralanmamış kutudaki parça küçük kutuya geçer**.
Bunu derleyin ve `main` fonksiyonundaki `ret` opcode'unda bir kesme noktası ile hata ayıklayın. Ardından `gef` ile tcache kutusunun dolu olduğunu ve bir parçanın küçük kutuda olduğunu görebilirsiniz:
```bash
gef➤ heap bins
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
Tcachebins[idx=15, size=0x110, count=7] ← Chunk(addr=0xaaaaaaac1d10, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1c00, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac1af0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac19e0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac18d0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac17c0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← Chunk(addr=0xaaaaaaac12a0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
───────────────────────────────────────────────────────────────────────── Fastbins for arena at 0xfffff7f90b00 ─────────────────────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
─────────────────────────────────────────────────────────────────────── Unsorted Bin for arena at 0xfffff7f90b00 ───────────────────────────────────────────────────────────────────────
[+] Found 0 chunks in unsorted bin.
──────────────────────────────────────────────────────────────────────── Small Bins for arena at 0xfffff7f90b00 ────────────────────────────────────────────────────────────────────────
[+] small_bins[16]: fw=0xaaaaaaac1e10, bk=0xaaaaaaac1e10
→ Chunk(addr=0xaaaaaaac1e20, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in 1 small non-empty bins.
Büyük kutular
Küçük kutuların sabit boyutlardaki parçaları yönettiği gibi, her büyük kutu bir dizi parça boyutunu yönetir. Bu daha esnektir ve sistemin çeşitli boyutları ayrı bir kutuya ihtiyaç duymadan karşılamasına olanak tanır.
Bir bellek ayırıcıda, büyük kutular küçük kutuların bittiği yerden başlar. Büyük kutuların aralıkları giderek büyür; bu, ilk kutunun 512 ile 576 byte arasındaki parçaları kapsayabileceği, bir sonraki kutunun ise 576 ile 640 byte arasındaki parçaları kapsayabileceği anlamına gelir. Bu desen devam eder ve en büyük kutu 1MB üzerindeki tüm parçaları içerir.
Büyük kutular, en iyi uyumu bulmak için farklı parça boyutları listesini sıralayıp aramak zorunda olduklarından küçük kutulara kıyasla daha yavaş çalışır. Bir parça büyük bir kutuya eklendiğinde, sıralanması gerekir ve bellek tahsis edildiğinde sistem doğru parçayı bulmalıdır. Bu ek iş, onları daha yavaş hale getirir, ancak büyük tahsisatlar küçük olanlardan daha az yaygın olduğundan, bu kabul edilebilir bir değiş tokuştur.
Şunlar vardır:
64B aralığında 32 kutu (küçük kutularla çakışır)
512B aralığında 16 kutu (küçük kutularla çakışır)
4096B aralığında 8 kutu (kısmen küçük kutularla çakışır)
32768B aralığında 4 kutu
262144B aralığında 2 kutu
Kalan boyutlar için 1 kutu
Büyük kutu boyutları kodu
```c // From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
</details>
<details>
<summary>Büyük bir parça örneği ekle</summary>
```c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *chunks[2];
chunks[0] = malloc(0x1500);
chunks[1] = malloc(0x1500);
free(chunks[0]);
chunks[0] = malloc(0x2000);
return 0;
}
2 büyük tahsis yapılır, ardından biri serbest bırakılır (bunu sıralanmamış kutuya koyar) ve daha büyük bir tahsis yapılır (serbest olanı sıralanmamış kutudan büyük kutuya taşır).
Bunu derleyin ve main fonksiyonundaki ret opcode'unda bir kesme noktası ile hata ayıklayın. Ardından gef ile tcache kutusunun dolu olduğunu ve bir parçanın büyük kutuda olduğunu görebilirsiniz:
gef➤heapbin────────────────────────────────────────────────────────────────────────────────Tcachebinsforthread1────────────────────────────────────────────────────────────────────────────────Alltcachebinsareempty─────────────────────────────────────────────────────────────────────────Fastbinsforarenaat0xfffff7f90b00─────────────────────────────────────────────────────────────────────────Fastbins[idx=0,size=0x20]0x00Fastbins[idx=1,size=0x30]0x00Fastbins[idx=2,size=0x40]0x00Fastbins[idx=3,size=0x50]0x00Fastbins[idx=4,size=0x60]0x00Fastbins[idx=5,size=0x70]0x00Fastbins[idx=6,size=0x80]0x00───────────────────────────────────────────────────────────────────────UnsortedBinforarenaat0xfffff7f90b00───────────────────────────────────────────────────────────────────────[+] Found 0 chunks in unsorted bin.────────────────────────────────────────────────────────────────────────SmallBinsforarenaat0xfffff7f90b00────────────────────────────────────────────────────────────────────────[+] Found 0 chunks in 0 small non-empty bins.────────────────────────────────────────────────────────────────────────LargeBinsforarenaat0xfffff7f90b00────────────────────────────────────────────────────────────────────────[+] large_bins[100]: fw=0xaaaaaaac1290, bk=0xaaaaaaac1290→Chunk(addr=0xaaaaaaac12a0, size=0x1510, flags=PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA)[+] Found 1 chunks in 1 large non-empty bins.
Üst Parça
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711/*TopThe top-most available chunk (i.e., the one bordering the end ofavailable memory) is treated specially. It is never included inany bin, is used only if no other chunk is available, and isreleased back to the system if it is very large (seeM_TRIM_THRESHOLD). Because top initiallypoints to its own bin with initial zero size, thus forcingextension on the first malloc request, we avoid having any specialcode in malloc to check whether it even exists yet. But we stillneed to do so when getting memory from system, so we makeinitial_top treat the bin as a legal but unusable chunk during theinterval between initialization and the first call tosysmalloc. (This is somewhat delicate, since it relies onthe 2 preceding words to be zero during this interval as well.)*//* Conveniently, the unsorted bin can be used as dummy top on first call */#defineinitial_top(M) (unsorted_chunks (M))
Temelde, bu mevcut tüm yığınları içeren bir parça. Bir malloc gerçekleştirildiğinde, kullanılacak herhangi bir boş parça yoksa, bu üst parça boyutunu azaltarak gerekli alanı sağlar.
Üst Parçaya işaretçi malloc_state yapısında saklanır.
Ayrıca, başlangıçta, sıralanmamış parçayı üst parça olarak kullanmak mümkündür.
Üst Parça örneğini gözlemleyin
```c #include #include
int main(void) { char *chunk; chunk = malloc(24); printf("Address of the chunk: %p\n", (void *)chunk); gets(chunk); return 0; }
Burada en üst parça adresinin 0xaaaaaaac1ae0 olduğu görülebilir. Bu sürpriz değil çünkü son tahsis edilen parça 0xaaaaaaac12a0 adresindeydi ve boyutu 0x410 idi ve 0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0 .
Ayrıca, en üst parçanın uzunluğunu parça başlığında görmek de mümkündür:
malloc kullanıldığında ve bir parça bölündüğünde (örneğin, sıralanmamış kutudan veya üst parçadan), bölünen parçanın geri kalanından oluşturulan parçaya Son Kalan denir ve işaretçisi malloc_state yapısında saklanır.