चंक्स को स्टोर करने की दक्षता में सुधार करने के लिए, हर चंक केवल एक लिंक्ड लिस्ट में नहीं होता, बल्कि कई प्रकार होते हैं। ये बिन हैं और 5 प्रकार के बिन होते हैं: 62 छोटे बिन, 63 बड़े बिन, 1 असॉर्टेड बिन, 10 फास्ट बिन और 64 टीकैश बिन प्रति थ्रेड।
प्रत्येक असॉर्टेड, छोटे और बड़े बिन के लिए प्रारंभिक पता उसी ऐरे के अंदर होता है। इंडेक्स 0 का उपयोग नहीं किया जाता, 1 असॉर्टेड बिन है, बिन 2-64 छोटे बिन हैं और बिन 65-127 बड़े बिन हैं।
टीकैश (प्रति-थ्रेड कैश) बिन
हालांकि थ्रेड्स अपनी खुद की हीप रखने की कोशिश करते हैं (देखें Arenas और Subheaps), एक प्रक्रिया जिसमें बहुत सारे थ्रेड्स होते हैं (जैसे एक वेब सर्वर) दूसरे थ्रेड्स के साथ हीप साझा करने की संभावना होती है। इस मामले में, मुख्य समाधान लॉकर का उपयोग है, जो थ्रेड्स को महत्वपूर्ण रूप से धीमा कर सकता है।
इसलिए, एक टीकैश एक फास्ट बिन की तरह होता है प्रति थ्रेड इस तरह से कि यह एक सिंगल लिंक्ड लिस्ट है जो चंक्स को मर्ज नहीं करता। प्रत्येक थ्रेड के पास 64 सिंगली-लिंक्ड टीकैश बिन होते हैं। प्रत्येक बिन में अधिकतम 7 समान आकार के चंक्स हो सकते हैं जो 64-बिट सिस्टम पर 24 से 1032B और 32-बिट सिस्टम पर 12 से 516B के बीच होते हैं।
जब एक थ्रेड एक चंक को फ्री करता है, यदि यह टीकैश में आवंटित करने के लिए बहुत बड़ा नहीं है और संबंधित टीकैश बिन भरा नहीं है (पहले से 7 चंक्स), तो इसे वहां आवंटित किया जाएगा। यदि यह टीकैश में नहीं जा सकता, तो इसे फ्री ऑपरेशन को वैश्विक रूप से करने के लिए हीप लॉक का इंतजार करना होगा।
जब एक चंक आवंटित किया जाता है, यदि आवश्यक आकार का एक फ्री चंक टीकैश में है तो इसका उपयोग करेगा, यदि नहीं, तो इसे वैश्विक बिन में एक खोजने या एक नया बनाने के लिए हीप लॉक का इंतजार करना होगा।
इस मामले में एक अनुकूलन भी है, जबकि हीप लॉक होने पर, थ्रेड अपने टीकैश को अनुरोधित आकार के हीप चंक्स (7) से भरेगा, ताकि यदि इसे और अधिक की आवश्यकता हो, तो इसे टीकैश में मिल जाएगा।
टीकैश चंक उदाहरण जोड़ें
```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; }
इसे संकलित करें और मुख्य फ़ंक्शन से ret ऑपकोड में एक ब्रेकपॉइंट के साथ डिबग करें। फिर gef के साथ आप उपयोग में tcache बिन देख सकते हैं:
```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 Structs & Functions
निम्नलिखित कोड में max bins और chunks per index देखना संभव है, tcache_entry स्ट्रक्चर जो डबल फ्री से बचने के लिए बनाया गया है और tcache_perthread_struct, एक स्ट्रक्चर जो प्रत्येक थ्रेड बिन के प्रत्येक इंडेक्स के पते को स्टोर करने के लिए उपयोग करता है।
tcache_entry और 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` वह फंक्शन है जो `tcache_perthread_struct` ऑब्जेक्ट के लिए स्थान बनाता और आवंटित करता है।
<details>
<summary>tcache_init कोड</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 इंडेक्स
Tcache में कई बिन होते हैं जो आकार और प्रत्येक इंडेक्स के पहले चंक के लिए प्रारंभिक पॉइंटर्स और प्रत्येक इंडेक्स के लिए चंक्स की मात्रा एक चंक के अंदर स्थित होती है। इसका मतलब है कि इस जानकारी के साथ चंक को ढूंढना (आमतौर पर पहला), सभी tcache प्रारंभिक बिंदुओं और Tcache चंक्स की मात्रा को ढूंढना संभव है।
फास्ट बिन
फास्ट बिन को छोटे चंक्स के लिए मेमोरी आवंटन को तेज करने के लिए डिज़ाइन किया गया है जो हाल ही में मुक्त किए गए चंक्स को त्वरित-एक्सेस संरचना में रखते हैं। ये बिन एक लास्ट-इन, फर्स्ट-आउट (LIFO) दृष्टिकोण का उपयोग करते हैं, जिसका अर्थ है कि हाल ही में मुक्त किया गया चंक पहले फिर से उपयोग किया जाता है जब एक नया आवंटन अनुरोध होता है। यह व्यवहार गति के लिए फायदेमंद है, क्योंकि एक स्टैक (LIFO) के शीर्ष से डालना और हटाना एक कतार (FIFO) की तुलना में तेज होता है।
इसके अतिरिक्त, फास्ट बिन सिंगली लिंक्ड लिस्ट का उपयोग करते हैं, डबल लिंक्ड नहीं, जो गति को और बढ़ाता है। चूंकि फास्ट बिन में चंक्स पड़ोसी के साथ विलय नहीं होते हैं, इसलिए मध्य से हटाने की अनुमति देने के लिए एक जटिल संरचना की आवश्यकता नहीं होती है। एक सिंगली लिंक्ड लिस्ट इन ऑपरेशनों के लिए सरल और तेज होती है।
बुनियादी रूप से, यहाँ जो होता है वह यह है कि हेडर (पहले चंक की जांच के लिए पॉइंटर) हमेशा उस आकार के नवीनतम मुक्त चंक की ओर इशारा कर रहा होता है। तो:
जब उस आकार का एक नया चंक आवंटित किया जाता है, तो हेडर एक मुक्त चंक की ओर इशारा कर रहा होता है। चूंकि यह मुक्त चंक उपयोग के लिए अगले चंक की ओर इशारा कर रहा है, यह पता हेडर में संग्रहीत किया जाता है ताकि अगली आवंटन को पता चले कि उपलब्ध चंक कहाँ से प्राप्त करना है।
जब एक चंक मुक्त किया जाता है, तो मुक्त चंक वर्तमान उपलब्ध चंक के पते को सहेजता है और इस नए मुक्त किए गए चंक का पता हेडर में रखा जाएगा।
एक लिंक्ड लिस्ट का अधिकतम आकार 0x80 है और इन्हें इस तरह से व्यवस्थित किया गया है कि 0x20 आकार का एक चंक इंडेक्स 0 में होगा, 0x30 आकार का एक चंक इंडेक्स 1 में होगा...
फास्ट बिन में चंक्स को उपलब्ध के रूप में सेट नहीं किया जाता है, इसलिए उन्हें कुछ समय के लिए फास्ट बिन चंक्स के रूप में रखा जाता है बजाय इसके कि वे चारों ओर के अन्य मुक्त चंक्स के साथ विलय कर सकें।
// 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)
एक फास्टबिन चंक उदाहरण जोड़ें
```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; }
ध्यान दें कि हम समान आकार के 8 चंक्स को कैसे आवंटित और मुक्त करते हैं ताकि वे tcache को भर दें और आठवां फास्ट चंक में संग्रहीत हो।
इसे संकलित करें और `main` फ़ंक्शन से `ret` ऑपकोड में एक ब्रेकपॉइंट के साथ डिबग करें। फिर `gef` के साथ आप देख सकते हैं कि tcache बिन भरा हुआ है और एक चंक फास्ट बिन में है:
```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
अनसॉर्टेड बिन
अनसॉर्टेड बिन एक कैश है जिसका उपयोग हीप प्रबंधक द्वारा मेमोरी आवंटन को तेज़ बनाने के लिए किया जाता है। यह इस प्रकार काम करता है: जब एक प्रोग्राम एक टुकड़ा मुक्त करता है, और यदि यह टुकड़ा एक tcache या फास्ट बिन में आवंटित नहीं किया जा सकता है और शीर्ष टुकड़े के साथ टकरा नहीं रहा है, तो हीप प्रबंधक तुरंत इसे किसी विशेष छोटे या बड़े बिन में नहीं रखता। इसके बजाय, यह पहले किसी भी पड़ोसी मुक्त टुकड़ों के साथ मिलाने की कोशिश करता है ताकि मुक्त मेमोरी का एक बड़ा ब्लॉक बनाया जा सके। फिर, यह इस नए टुकड़े को "अनसॉर्टेड बिन" नामक एक सामान्य बिन में रखता है।
जब एक प्रोग्राम मेमोरी के लिए पूछता है, तो हीप प्रबंधक अनसॉर्टेड बिन की जांच करता है यह देखने के लिए कि क्या वहाँ पर्याप्त आकार का एक टुकड़ा है। यदि यह एक पाता है, तो इसका तुरंत उपयोग करता है। यदि इसे अनसॉर्टेड बिन में उपयुक्त टुकड़ा नहीं मिलता है, तो यह इस सूची में सभी टुकड़ों को उनके आकार के आधार पर छोटे या बड़े बिन में स्थानांतरित कर देता है।
ध्यान दें कि यदि एक बड़ा टुकड़ा 2 आधों में विभाजित किया जाता है और बाकी MINSIZE से बड़ा है, तो इसे अनसॉर्टेड बिन में वापस रखा जाएगा।
तो, अनसॉर्टेड बिन हाल ही में मुक्त की गई मेमोरी को तेजी से पुन: उपयोग करके मेमोरी आवंटन को तेज़ करने का एक तरीका है और समय-खपत करने वाली खोजों और विलय की आवश्यकता को कम करता है।
ध्यान दें कि भले ही टुकड़े विभिन्न श्रेणियों के हों, यदि एक उपलब्ध टुकड़ा दूसरे उपलब्ध टुकड़े के साथ टकरा रहा है (भले ही वे मूल रूप से विभिन्न बिनों से संबंधित हों), तो उन्हें मिलाया जाएगा।
अनसॉर्टेड टुकड़े का उदाहरण जोड़ें
```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; }
ध्यान दें कि हम एक ही आकार के 9 चंक्स को कैसे आवंटित और मुक्त करते हैं ताकि वे **tcache को भर दें** और आठवां अनसॉर्टेड बिन में संग्रहीत होता है क्योंकि यह **fastbin के लिए बहुत बड़ा है** और नौवां मुक्त नहीं किया गया है इसलिए नौवां और आठवां **शीर्ष चंक के साथ विलय नहीं होते**।
इसे संकलित करें और `main` फ़ंक्शन से `ret` ऑपकोड में एक ब्रेकपॉइंट के साथ डिबग करें। फिर `gef` के साथ आप देख सकते हैं कि tcache बिन भरा हुआ है और एक चंक अनसॉर्टेड बिन में है:
```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.
छोटे बिन
छोटे बिन बड़े बिनों की तुलना में तेज होते हैं लेकिन तेज बिनों की तुलना में धीमे होते हैं।
62 के प्रत्येक बिन में एक ही आकार के टुकड़े होंगे: 16, 24, ... (32-बिट में अधिकतम आकार 504 बाइट और 64-बिट में 1024)। यह उस बिन को खोजने की गति में मदद करता है जहाँ स्थान आवंटित किया जाना चाहिए और इन सूचियों पर प्रविष्टियों को डालने और हटाने में।
यह इस तरह से है कि छोटे बिन का आकार बिन के अनुक्रमांक के अनुसार गणना की जाती है:
सबसे छोटा आकार: 2*4*अनुक्रमांक (जैसे अनुक्रमांक 5 -> 40)
सबसे बड़ा आकार: 2*8*अनुक्रमांक (जैसे अनुक्रमांक 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; }
नोट करें कि हम एक ही आकार के 9 चंक्स को कैसे आवंटित और मुक्त करते हैं ताकि वे **tcache को भर दें** और आठवां अनसॉर्टेड बिन में संग्रहीत होता है क्योंकि यह **fastbin के लिए बहुत बड़ा है** और नौवां मुक्त नहीं किया गया है इसलिए नौवां और आठवां **शीर्ष चंक के साथ विलय नहीं होते**। फिर हम 0x110 का एक बड़ा चंक आवंटित करते हैं जो **अनसॉर्टेड बिन में चंक को छोटे बिन में ले जाता है**।
इसे संकलित करें और `main` फ़ंक्शन के `ret` ऑपकोड में एक ब्रेकपॉइंट के साथ डिबग करें। फिर `gef` के साथ आप देख सकते हैं कि tcache बिन भरा हुआ है और एक चंक छोटे बिन में है:
```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.
Large bins
छोटे बिनों के विपरीत, जो निश्चित आकार के टुकड़ों का प्रबंधन करते हैं, प्रत्येक large bin एक श्रृंखला के टुकड़ों के आकार को संभालता है। यह अधिक लचीला है, जिससे सिस्टम को विभिन्न आकारों को समायोजित करने की अनुमति मिलती है बिना प्रत्येक आकार के लिए एक अलग बिन की आवश्यकता के।
एक मेमोरी आवंटक में, बड़े बिन छोटे बिनों के समाप्त होने पर शुरू होते हैं। बड़े बिनों के लिए रेंज धीरे-धीरे बढ़ती है, जिसका अर्थ है कि पहला बिन 512 से 576 बाइट्स तक के टुकड़ों को कवर कर सकता है, जबकि अगला 576 से 640 बाइट्स तक कवर करता है। यह पैटर्न जारी रहता है, सबसे बड़े बिन में 1MB से ऊपर के सभी टुकड़े होते हैं।
बड़े बिन छोटे बिनों की तुलना में संचालन में धीमे होते हैं क्योंकि उन्हें आवंटन के लिए सर्वश्रेष्ठ फिट खोजने के लिए विभिन्न टुकड़ों के आकार की सूची को क्रमबद्ध और खोजने की आवश्यकता होती है। जब एक टुकड़ा बड़े बिन में डाला जाता है, तो इसे क्रमबद्ध करना होता है, और जब मेमोरी आवंटित की जाती है, तो सिस्टम को सही टुकड़ा खोजना होता है। यह अतिरिक्त कार्य उन्हें धीमा बनाता है, लेकिन चूंकि बड़े आवंटन छोटे आवंटनों की तुलना में कम सामान्य होते हैं, यह एक स्वीकार्य व्यापार-बंद है।
यहाँ हैं:
32 बिन 64B रेंज के (छोटे बिनों के साथ टकराते हैं)
16 बिन 512B रेंज के (छोटे बिनों के साथ टकराते हैं)
8 बिन 4096B रेंज के (कुछ छोटे बिनों के साथ टकराते हैं)
4 बिन 32768B रेंज के
2 बिन 262144B रेंज के
1 बिन शेष आकारों के लिए
Large bin sizes code
```c // From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
</details>
<details>
<summary>एक बड़ा टुकड़ा उदाहरण जोड़ें</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 बड़े आवंटन किए जाते हैं, फिर एक को मुक्त किया जाता है (इसे असंरचित बिन में डालते हुए) और एक बड़ा आवंटन किया जाता है (मुक्त एक को असंरचित बिन से बड़े बिन में ले जाते हुए)।
इसे संकलित करें और main फ़ंक्शन से ret ऑपकोड में एक ब्रेकपॉइंट के साथ डिबग करें। फिर gef के साथ आप देख सकते हैं कि tcache बिन भरा हुआ है और एक टुकड़ा बड़े बिन में है:
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.
शीर्ष भाग
// 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))
बुनियादी रूप से, यह एक टुकड़ा है जिसमें सभी वर्तमान में उपलब्ध हीप शामिल हैं। जब malloc किया जाता है, यदि उपयोग करने के लिए कोई उपलब्ध मुक्त टुकड़ा नहीं है, तो यह शीर्ष टुकड़ा अपना आकार घटा देगा आवश्यक स्थान देने के लिए।
Top Chunk का पॉइंटर malloc_state संरचना में संग्रहीत होता है।
इसके अलावा, शुरुआत में, अनसॉर्टेड टुकड़े का उपयोग शीर्ष टुकड़े के रूप में किया जा सकता है।
Top Chunk उदाहरण पर ध्यान दें
```c #include #include
int main(void) { char *chunk; chunk = malloc(24); printf("Address of the chunk: %p\n", (void *)chunk); gets(chunk); return 0; }
`main` के `ret` ऑपकोड में ब्रेक पॉइंट के साथ इसे संकलित और डिबग करने के बाद मैंने देखा कि malloc ने पता `0xaaaaaaac12a0` लौटाया और ये चंक्स हैं:
```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
जहाँ यह देखा जा सकता है कि शीर्ष टुकड़ा पते 0xaaaaaaac1ae0 पर है। यह कोई आश्चर्य की बात नहीं है क्योंकि अंतिम आवंटित टुकड़ा 0xaaaaaaac12a0 पर था जिसका आकार 0x410 था और 0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0।
यह भी संभव है कि शीर्ष टुकड़े की लंबाई इसके टुकड़े के हेडर पर देखी जा सके:
जब malloc का उपयोग किया जाता है और एक टुकड़ा विभाजित किया जाता है (उदाहरण के लिए, असंरचित बिन से या शीर्ष टुकड़े से), तो विभाजित टुकड़े के शेष से बनाए गए टुकड़े को अंतिम शेष कहा जाता है और इसका पॉइंटर malloc_state संरचना में संग्रहीत होता है।
आवंटन प्रवाह
चेक करें:
मुक्त प्रवाह
चेक करें:
हीप फ़ंक्शंस सुरक्षा जांच
हीप में भारी उपयोग किए जाने वाले फ़ंक्शंस द्वारा किए गए सुरक्षा जांचों की जांच करें: