Bins & Memory Allocations

Support HackTricks

Taarifa Msingi

Ili kuboresha ufanisi wa jinsi vipande vinavyohifadhiwa, kila kipande sio tu kwenye orodha moja iliyounganishwa, bali kuna aina kadhaa. Hizi ni vitengo vya kumbukumbu na kuna aina 5 za vitengo: 62 vitengo vidogo, 63 vitengo vikubwa, 1 kikapu kisichopangwa, 10 vitengo vya haraka na vitengo vya tcache 64 kwa kila mnyororo.

Anwani ya awali ya kila kikapu kisichopangwa, kidogo na kikubwa iko ndani ya safu moja. Indeksi 0 haifai, 1 ni kikapu kisichopangwa, vikapu 2-64 ni vitengo vidogo na vikapu 65-127 ni vitengo vikubwa.

Vitengo vya Tcache (Akiba kwa Kila Mnyororo)

Ingawa nyuzi zinajaribu kuwa na kumbukumbu yao wenyewe (angalia Arenas na Subheaps), kuna uwezekano kwamba mchakato na nyuzi nyingi (kama seva ya wavuti) itamaliza kushiriki kumbukumbu na nyuzi nyingine. Katika kesi hii, suluhisho kuu ni matumizi ya kifungio, ambacho kinaweza kupunguza kwa kiasi kikubwa kasi ya nyuzi.

Kwa hivyo, tcache inafanana na kikapu cha haraka kwa kila nyuzi kwa njia kwamba ni orodha moja iliyounganishwa ambayo haifungi vipande. Kila nyuzi ina vitengo vya tcache vilivyounganishwa kwa mnyororo mmoja. Kila kikapu kinaweza kuwa na kiwango cha juu cha vipande vya saizi sawa 7 kuanzia 24 hadi 1032B kwenye mifumo ya 64-bit na 12 hadi 516B kwenye mifumo ya 32-bit.

Wakati nyuzi inaachilia kipande, ikiwa sio kikubwa sana kuweza kupangwa kwenye tcache na kikapu cha tcache hakijajaa (tayari kuna vipande 7), itapangwa hapo. Ikiwa haitaweza kwenda kwenye tcache, italazimika kusubiri kifungio cha kumbukumbu ili iweze kufanya operesheni ya kuachilia kwa jumla.

Wakati kipande kinapopangwa, ikiwa kuna kipande cha bure cha saizi inayohitajika kwenye Tcache itakitumia, la sivyo, italazimika kusubiri kifungio cha kumbukumbu ili iweze kupata moja kwenye vitengo vya jumla au kuunda mpya. Kuna pia uboreshaji, katika kesi hii, wakati wa kuwa na kifungio cha kumbukumbu, nyuzi itajaza Tcache yake na vipande vya kumbukumbu (7) vya kumbukumbu ya ombi ya saizi, hivyo ikihitaji zaidi, itavipata kwenye Tcache.

Ongeza mfano wa kipande cha tcache

```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; }

Fanya kompile na uishe na kosa katika opcode ya ret kutoka kwenye kazi kuu. Kisha kwa kutumia gef unaweza kuona tcache bin inayotumiwa:
```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 Miundo na Kazi

Katika msimbo ufuatao inawezekana kuona max bins na vipande kwa kila index, miundo ya tcache_entry iliyoanzishwa ili kuepuka kufuta mara mbili na tcache_perthread_struct, miundo ambayo kila mnyororo hutumia kuhifadhi anwani za kila index ya bakuli.

tcache_entry na 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. */

define TCACHE_MAX_BINS 64

define MAX_TCACHE_SIZE tidx2usize (TCACHE_MAX_BINS-1)

/* Only used to pre-fill the tunables. */

define tidx2usize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)

/* When "x" is from chunksize(). */

define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)

/* When "x" is a user-provided size. */

define usize2tidx(x) csize2tidx (request2size (x))

/* 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>

Kazi ya `__tcache_init` ni kazi inayounda na kutenga nafasi kwa ajili ya obj ya `tcache_perthread_struct`
```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));
}

}

Viashiria vya Tcache

Tcache ina viashiria kadhaa kulingana na ukubwa na viashiria vya awali kwa kila kipande cha kwanza cha kila kiashiria na idadi ya vipande kwa kila kiashiria vimehifadhiwa ndani ya kipande. Hii inamaanisha kwamba kwa kutambua kipande chenye habari hii (kawaida cha kwanza), ni rahisi kupata viashiria vyote vya tcache na idadi ya vipande vya Tcache.

Bins za Haraka

Bins za haraka zimeundwa ili kuongeza kasi ya kutengwa kwa kumbukumbu kwa vipande vidogo kwa kuweka vipande vilivyotolewa hivi karibuni katika muundo wa kupata haraka. Bins hizi hutumia njia ya Mwisho-Kuingia, Mwanzo-Kutoka (LIFO), ambayo inamaanisha kwamba kipande kilichotolewa hivi karibuni zaidi ndicho kwanza kutumika tena wakati kuna ombi jipya la kutengwa. Tabia hii ni faida kwa kasi, kwani ni haraka kuweka na kuondoa kutoka juu ya rundo (LIFO) ikilinganishwa na foleni (FIFO).

Kwa kuongezea, bins za haraka hutumia orodha za viungo moja tu, sio viungo vya mara mbili, ambavyo vinaboresha kasi zaidi. Kwa kuwa vipande katika bins za haraka havijunganishwi na majirani, hakuna haja ya muundo tata unaoruhusu kuondolewa katikati. Orodha ya viungo moja ni rahisi na haraka kwa operesheni hizi.

Kimsingi, kinachotokea hapa ni kwamba kichwa (kiashiria cha kipande cha kwanza cha kuangalia) kila wakati kinaelekeza kwenye kipande kilichotolewa hivi karibuni zaidi cha ukubwa huo. Kwa hivyo:

  • Wakati kipande kipya kinatengwa cha ukubwa huo, kichwa kinatazama kipande huru cha kutumia. Kwa kuwa kipande hiki huru kinatazama kipande kinachofuata kutumika, anwani hii imehifadhiwa kwenye kichwa ili kutambua wapi kupata kipande kinachopatikana

  • Wakati kipande kinapotolewa, kipande huru kitahifadhi anwani ya kipande kinachopatikana kwa sasa na anwani ya kipande hiki kilichotolewa hivi karibuni itawekwa kwenye kichwa

Ukubwa wa juu wa orodha ya viungo ni 0x80 na zimepangwa ili kipande cha ukubwa wa 0x20 kiwe katika kiashiria 0, kipande cha ukubwa wa 0x30 kingekuwa katika kiashiria 1...

Vipande katika bins za haraka havijawekwa kama vinavyopatikana kwa hivyo vinabaki kama vipande vya bins za haraka kwa muda fulani badala ya kuweza kuunganishwa na vipande vingine huru vinavyowazunguka.

// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711

/*
Fastbins

An array of lists holding recently freed small chunks.  Fastbins
are not doubly linked.  It is faster to single-link them, and
since chunks are never removed from the middles of these lists,
double linking is not necessary. Also, unlike regular bins, they
are not even processed in FIFO order (they use faster LIFO) since
ordering doesn't much matter in the transient contexts in which
fastbins are normally used.

Chunks in fastbins keep their inuse bit set, so they cannot
be consolidated with other free chunks. malloc_consolidate
releases all chunks in fastbins and consolidates them with
other free chunks.
*/

typedef struct malloc_chunk *mfastbinptr;
#define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx])

/* offset 2 to use otherwise unindexable first 2 bins */
#define fastbin_index(sz) \
((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)


/* The maximum fastbin request size we support */
#define MAX_FAST_SIZE     (80 * SIZE_SZ / 4)

#define NFASTBINS  (fastbin_index (request2size (MAX_FAST_SIZE)) + 1)
Ongeza mfano wa kipande cha fastbin

```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; }

Tambua jinsi tunavyo allocate na free vipande 8 vya saizi ileile ili wajaze tcache na cha nane kuhifadhiwa kwenye kisanduku cha haraka.

Sakinisha na ukague kwa kusitisha kwenye opcode ya `ret` kutoka kwenye kazi ya `main`. Kisha kwa kutumia `gef` unaweza kuona kuwa kisanduku cha tcache kimejaa na kipande kimoja kipo kwenye kisanduku cha haraka:
```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

Bakuli lisilo na mpangilio

Bakuli lisilo na mpangilio ni akiba inayotumiwa na meneja wa bakuli ili kufanya mgawanyo wa kumbukumbu uwe haraka zaidi. Hapa ndivyo inavyofanya kazi: Wakati programu inaachilia kipande, na ikiwa kipande hiki hakiwezi kupewa katika akiba ya tcache au bakuli la haraka na haligongani na kipande cha juu, meneja wa bakuli hauweki mara moja katika bakuli maalum la kipande kidogo au kikubwa. Badala yake, kwanza inajaribu kulifunga na vipande vingine vya bure vya jirani ili kuunda kibodi kubwa ya kumbukumbu ya bure. Kisha, inaweka kipande kipya hiki katika bakuli la jumla linaloitwa "bakuli lisilo na mpangilio."

Wakati programu inahitaji kumbukumbu, meneja wa bakuli huchunguza bakuli lisilo na mpangilio kuona ikiwa kuna kipande cha ukubwa wa kutosha. Ikiipata, inakitumia mara moja. Ikiwa haitapata kipande kinachofaa katika bakuli lisilo na mpangilio, inahamisha vipande vyote katika orodha hii kwenye bakuli zao husika, iwe ni kipande kidogo au kikubwa, kulingana na ukubwa wao.

Tafadhali kumbuka kwamba ikiwa kipande kikubwa kimegawanywa katika sehemu 2 na sehemu iliyobaki ni kubwa kuliko MINSIZE, itarudishwa tena kwenye bakuli lisilo na mpangilio.

Kwa hivyo, bakuli lisilo na mpangilio ni njia ya kuharakisha mgawanyo wa kumbukumbu kwa haraka kutumia tena kumbukumbu iliyotolewa hivi karibuni na kupunguza haja ya kutafuta na kufunga kwa muda mrefu.

Tafadhali kumbuka kwamba hata ikiwa vipande ni vya makundi tofauti, ikiwa kipande kinachopatikana kinagongana na kipande kingine kinachopatikana (hata ikiwa awali zilikuwa katika bakuli tofauti), vitafungwa pamoja.

Ongeza mfano wa kipande kilichochanganyikana

```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; }

Tazama jinsi tunavyo alokei na kuachilia vipande 9 vya saizi ile ile ili vijaze tcache na cha nane kuhifadhiwa kwenye unsorted bin kwa sababu ni kubwa mno kwa fastbin na cha tisa hakijaachiliwa hivyo cha tisa na cha nane havijunganishwa na top chunk.

Sakinisha na uisashe na kuvunja kwa kizuizi katika opcode ya `ret` kutoka kwenye kazi ya `main`. Kisha kwa kutumia `gef` unaweza kuona kuwa tcache bin imejaa na kipande kimoja kipo kwenye unsorted bin:
```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.

Vifurushi Vidogo

Vifurushi vidogo ni haraka kuliko vifurushi vikubwa lakini polepole kuliko vifurushi vya haraka.

Kila kifurushi kati ya 62 kitakuwa na vipande vya saizi ile ile: 16, 24, ... (na saizi ya juu ya 504 baiti katika bits 32 na 1024 katika bits 64). Hii husaidia katika kasi ya kupata kifurushi ambapo nafasi inapaswa kutengwa na kuingiza na kuondoa vipande kwenye orodha hizi.

Hivi ndivyo saizi ya kifurushi kidogo inavyohesabiwa kulingana na index ya kifurushi:

  • Saizi ndogo zaidi: 2*4*index (k.m. index 5 -> 40)

  • Saizi kubwa zaidi: 2*8*index (k.m. index 5 -> 80)

// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
#define NSMALLBINS         64
#define SMALLBIN_WIDTH    MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > CHUNK_HDR_SZ)
#define MIN_LARGE_SIZE    ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)

#define in_smallbin_range(sz)  \
((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)

#define smallbin_index(sz) \
((SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) : (((unsigned) (sz)) >> 3))\
+ SMALLBIN_CORRECTION)

Function ya kuchagua kati ya mabano madogo na makubwa:

#define bin_index(sz) \
((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))
Wekeza mfano wa kipande kidogo

```c #include #include

int main(void) { char *chunks[10]; 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]); }

chunks[9] = malloc(0x110);

return 0; }

Tambua jinsi tunavyo tenganisha na kuachilia vipande 9 vya saizi ile ile ili **kujaza tcache** na cha nane kuhifadhiwa kwenye sanduku lisilo na mpangilio kwa sababu ni **kubwa sana kwa fastbin** na cha tisa hakijatolewa hivyo cha tisa na cha nane **haviunganishwi na kipande cha juu**. Kisha tunatenga kipande kikubwa cha 0x110 ambacho kinasababisha **kipande kwenye sanduku lisilo na mpangilio kuingia kwenye sanduku dogo**.

Sakinisha na ugundue kwa kuweka kizuizi katika opcode ya `ret` kutoka kwenye kazi ya `main`. Kisha kwa kutumia `gef` unaweza kuona kuwa sanduku la tcache limejaa na kipande kimoja kipo kwenye sanduku dogo:
```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.

Bins kubwa

Tofauti na bins ndogo, ambazo hushughulikia vipande vya saizi iliyowekwa, kila bin kubwa hushughulikia safu ya saizi za vipande. Hii ni ya kubadilika zaidi, ikiruhusu mfumo kuzoea saizi tofauti bila kuhitaji bin tofauti kwa kila saizi.

Katika mpangishaji wa kumbukumbu, bins kubwa huanza pale ambapo bins ndogo zinaishia. Safu za bins kubwa hukua kwa ukubwa kwa mfululizo, maana bin ya kwanza inaweza kufunika vipande kutoka 512 hadi 576 baiti, wakati inayofuata inafunika 576 hadi 640 baiti. Mtindo huu unaendelea, na bin kubwa zaidi ina vipande vyote zaidi ya 1MB.

Bins kubwa ni polepole kufanya kazi ikilinganishwa na bins ndogo kwa sababu lazima zipange na kutafuta kupitia orodha ya saizi tofauti za vipande ili kupata saizi bora kwa mgawo. Wakati kipande kinawekwa kwenye bin kubwa, lazima kipangwe, na wakati kumbukumbu inapogawiwa, mfumo lazima upate kipande sahihi. Kazi hii ya ziada inawafanya kuwa polepole, lakini kwa kuwa mgawo wa kubwa ni nadra kuliko vipande vidogo, ni sawa kufanya hivyo.

Kuna:

  • Bins 32 za safu ya 64B (zinagongana na bins ndogo)

  • Bins 16 za safu ya 512B (zinagongana na bins ndogo)

  • Bins 8 za safu ya 4096B (sehemu inagongana na bins ndogo)

  • Bins 4 za safu ya 32768B

  • Bins 2 za safu ya 262144B

  • Bin 1 kwa saizi zilizosalia

Msimbo wa Saizi za Bins Kubwa

```c // From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711

#define largebin_index_32(sz) (((((unsigned long) (sz)) >> 6) <= 38) ? 56 + (((unsigned long) (sz)) >> 6) : ((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) : ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) : ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) : ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) : 126)

#define largebin_index_32_big(sz) (((((unsigned long) (sz)) >> 6) <= 45) ? 49 + (((unsigned long) (sz)) >> 6) : ((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) : ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) : ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) : ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) : 126)

// XXX It remains to be seen whether it is good to keep the widths of // XXX the buckets the same or whether it should be scaled by a factor // XXX of two as well. #define largebin_index_64(sz) (((((unsigned long) (sz)) >> 6) <= 48) ? 48 + (((unsigned long) (sz)) >> 6) : ((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) : ((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) : ((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) : ((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) : 126)

#define largebin_index(sz) (SIZE_SZ == 8 ? largebin_index_64 (sz) : MALLOC_ALIGNMENT == 16 ? largebin_index_32_big (sz) : largebin_index_32 (sz))

</details>

<details>

<summary>Wekeza mfano wa kipande kikubwa</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 alokesheni kubwa zinafanywa, kisha moja inaachiliwa (ikiwekwa kwenye sanduku lisilo na mpangilio) na alokesheni kubwa zaidi inafanywa (ikihamisha ile iliyofutwa kutoka kwenye sanduku lisilo na mpangilio kwenda kwenye sanduku kubwa).

Sakinisha na uisashe na kizuizi katika opcode ya ret kutoka kwenye kazi ya kuu. kisha kwa kutumia gef unaweza kuona kuwa sanduku la tcache limejaa na kipande kimoja kipo kwenye sanduku kubwa:

gef➤  heap bin
──────────────────────────────────────────────────────────────────────────────── Tcachebins for thread 1 ────────────────────────────────────────────────────────────────────────────────
All tcachebins are empty
───────────────────────────────────────────────────────────────────────── 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 ────────────────────────────────────────────────────────────────────────
[+] Found 0 chunks in 0 small non-empty bins.
──────────────────────────────────────────────────────────────────────── Large Bins for arena at 0xfffff7f90b00 ────────────────────────────────────────────────────────────────────────
[+] 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.

Kipande cha Juu

// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711

/*
Top

The top-most available chunk (i.e., the one bordering the end of
available memory) is treated specially. It is never included in
any bin, is used only if no other chunk is available, and is
released back to the system if it is very large (see
M_TRIM_THRESHOLD).  Because top initially
points to its own bin with initial zero size, thus forcing
extension on the first malloc request, we avoid having any special
code in malloc to check whether it even exists yet. But we still
need to do so when getting memory from system, so we make
initial_top treat the bin as a legal but unusable chunk during the
interval between initialization and the first call to
sysmalloc. (This is somewhat delicate, since it relies on
the 2 preceding words to be zero during this interval as well.)
*/

/* Conveniently, the unsorted bin can be used as dummy top on first call */
#define initial_top(M)              (unsorted_chunks (M))

Kimsingi, hii ni kipande kinachojumuisha kila kipande cha heap kinachopatikana kwa sasa. Wakati malloc inafanywa, ikiwa hakuna kipande cha bure kinachopatikana kutumika, kipande hiki cha juu kitapunguza ukubwa wake kutoa nafasi inayohitajika. Pointer kwa Kipande cha Juu kuhifadhiwa katika muundo wa malloc_state.

Zaidi ya hayo, mwanzoni, ni sawa kutumia kipande kilichochanganyika kama kipande cha juu.

Angalia mfano wa Kipande cha Juu

```c #include #include

int main(void) { char *chunk; chunk = malloc(24); printf("Address of the chunk: %p\n", (void *)chunk); gets(chunk); return 0; }

Baada ya kuiandaa na kuibugia na kivinjari katika opcode ya `ret` ya `main` niliona kwamba malloc ilirudisha anwani `0xaaaaaaac12a0` na hizi ni vipande:
```bash
gef➤  heap chunks
Chunk(addr=0xaaaaaaac1010, size=0x290, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac1010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12a0     41 41 41 41 41 41 41 00 00 00 00 00 00 00 00 00    AAAAAAA.........]
Chunk(addr=0xaaaaaaac12c0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12c0     41 64 64 72 65 73 73 20 6f 66 20 74 68 65 20 63    Address of the c]
Chunk(addr=0xaaaaaaac16d0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac16d0     41 41 41 41 41 41 41 0a 00 00 00 00 00 00 00 00    AAAAAAA.........]
Chunk(addr=0xaaaaaaac1ae0, size=0x20530, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)  ←  top chunk

Mahali ambapo inaweza kuonekana kuwa kipande cha juu kiko kwenye anwani 0xaaaaaaac1ae0. Hii si mshangao kwa sababu kipande cha mwisho kilichotengwa kilikuwa kwenye 0xaaaaaaac12a0 na ukubwa wa 0x410 na 0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0. Pia ni sawa kuona urefu wa kipande cha Juu kwenye kichwa chake cha kipande:

gef➤  x/8wx 0xaaaaaaac1ae0 - 16
0xaaaaaaac1ad0:	0x00000000	0x00000000	0x00020531	0x00000000
0xaaaaaaac1ae0:	0x00000000	0x00000000	0x00000000	0x00000000

Kukaa Mwisho

Wakati malloc inapotumiwa na kipande kinagawanywa (kutoka kwa sanduku lisilo na mpangilio au kutoka kwa kipande cha juu kwa mfano), kipande kilichoundwa kutoka kwa sehemu iliyobaki ya kipande kilichogawanywa huitwa Kukaa Mwisho na pointer yake hifadhiwa katika muundo wa malloc_state.

Mzunguko wa Kutengwa

Angalia:

malloc & sysmalloc

Mzunguko wa Kuachiliwa

Angalia:

free

Uchunguzi wa Usalama wa Kazi za Sanduku

Angalia uchunguzi wa usalama uliofanywa na kazi zinazotumiwa sana katika sanduku katika:

Heap Functions Security Checks

Marejeo

Support HackTricks

Last updated