malloc & sysmalloc

Підтримайте HackTricks

Огляд порядку виділення

(У цьому огляді не пояснюються перевірки, і деякі випадки були пропущені для кратності)

  1. __libc_malloc намагається отримати фрагмент з tcache, якщо немає, воно викликає _int_malloc

  2. _int_malloc :

  3. Намагається створити арену, якщо її немає

  4. Якщо є будь-який швидкий фрагмент відповідного розміру, використовуйте його

  5. Заповніть tcache іншими швидкими фрагментами

  6. Якщо є будь-який фрагмент малих бінів відповідного розміру, використовуйте його

  7. Заповніть tcache іншими фрагментами цього розміру

  8. Якщо запитаний розмір не для малих бінів, об'єднайте швидкий бін в неупорядкований бін

  9. Перевірте неупорядкований бін, використовуйте перший фрагмент з достатньою пам'яттю

  10. Якщо знайдений фрагмент більший, розділіть його, щоб повернути частину та додати нагадування назад до неупорядкованого біна

  11. Якщо фрагмент має такий самий розмір, як запитаний розмір, використовуйте його для заповнення tcache замість повернення його (доки tcache не буде повна, тоді поверніть наступний)

  12. Для кожного перевіреного фрагмента меншого розміру, розмістіть його в відповідний малий або великий бін

  13. Перевірте великий бін за індексом запитаного розміру

  14. Почніть переглядати з першого фрагмента, який більший за запитаний розмір, якщо знайдете, поверніть його та додайте нагадування до малого біна

  15. Перевірте великі біни з наступних індексів до кінця

  16. З наступного більшого індексу перевірте будь-який фрагмент, розділіть перший знайдений фрагмент, щоб використовувати його для запитаного розміру та додати нагадування до неупорядкованого біна

  17. Якщо в попередніх бінах нічого не знайдено, отримайте фрагмент з верхнього фрагмента

  18. Якщо верхній фрагмент не був достатньо великим, збільште його за допомогою sysmalloc

__libc_malloc

Функція malloc фактично викликає __libc_malloc. Ця функція перевіряє tcache, щоб переконатися, чи є доступний фрагмент потрібного розміру. Якщо є, вона використовує його, а якщо немає, вона перевіряє, чи це однопотокове середовище, і в цьому випадку вона викликає _int_malloc в основній арені, а якщо ні, вона викликає _int_malloc в арені потоку.

Код __libc_malloc

```c // From https://github.com/bminor/glibc/blob/master/malloc/malloc.c

#if IS_IN (libc) void * __libc_malloc (size_t bytes) { mstate ar_ptr; void *victim;

_Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2, "PTRDIFF_MAX is not more than half of SIZE_MAX");

if (!__malloc_initialized) ptmalloc_init (); #if USE_TCACHE /* int_free also calls request2size, be careful to not pad twice. */ size_t tbytes = checked_request2size (bytes); if (tbytes == 0) { __set_errno (ENOMEM); return NULL; } size_t tc_idx = csize2tidx (tbytes);

MAYBE_INIT_TCACHE ();

DIAG_PUSH_NEEDS_COMMENT; if (tc_idx < mp_.tcache_bins && tcache != NULL && tcache->counts[tc_idx] > 0) { victim = tcache_get (tc_idx); return tag_new_usable (victim); } DIAG_POP_NEEDS_COMMENT; #endif

if (SINGLE_THREAD_P) { victim = tag_new_usable (_int_malloc (&main_arena, bytes)); assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || &main_arena == arena_for_chunk (mem2chunk (victim))); return victim; }

arena_get (ar_ptr, bytes);

victim = _int_malloc (ar_ptr, bytes); /* Retry with another arena only if we were able to find a usable arena before. */ if (!victim && ar_ptr != NULL) { LIBC_PROBE (memory_malloc_retry, 1, bytes); ar_ptr = arena_get_retry (ar_ptr, bytes); victim = _int_malloc (ar_ptr, bytes); }

if (ar_ptr != NULL) __libc_lock_unlock (ar_ptr->mutex);

victim = tag_new_usable (victim);

assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || ar_ptr == arena_for_chunk (mem2chunk (victim))); return victim; }

</details>

Зверніть увагу, що завжди буде використовуватися тег `tag_new_usable` для поверненого вказівника, з коду:
```c
void *tag_new_usable (void *ptr)

Allocate a new random color and use it to color the user region of
a chunk; this may include data from the subsequent chunk's header
if tagging is sufficiently fine grained.  Returns PTR suitably
recolored for accessing the memory there.

_int_malloc

Це функція, яка виділяє пам'ять, використовуючи інші біни та верхній фрагмент.

  • Початок

Вона починається з визначення деяких змінних та отримання реального розміру, який потрібно виділити для запиту пам'яті:

Швидкий бін

Якщо потрібний розмір знаходиться в межах розмірів Fast Bins, спробуйте використати частину з швидкого біну. Основна ідея полягає в тому, що відповідно до розміру він знаходить індекс швидкого біну, де повинні знаходитися дійсні частини, і якщо такі є, повертає одну з них. Крім того, якщо включений tcache, він заповнить tcache бін такого розміру швидкими бінами.

Під час виконання цих дій тут виконуються деякі перевірки безпеки:

  • Якщо частина не вирівняна: malloc(): виявлено невирівняну швидку частину 2

  • Якщо наступна частина не вирівняна: malloc(): виявлено невирівняну швидку частину

  • Якщо повернута частина має неправильний розмір через її індекс у швидкому біні: malloc(): корупція пам'яті (швидка)

  • Якщо будь-яка частина, використана для заповнення tcache, не вирівняна: malloc(): виявлено невирівняну швидку частину 3

malloc_consolidate

Якщо це не був невеликий фрагмент, то це великий фрагмент, і в цьому випадку викликається malloc_consolidate, щоб уникнути фрагментації пам'яті.

Невідсортований бін

Час перевірити невідсортований бін на наявність потенційного дійсного чанку для використання.

Початок

Це починається з великого циклу, який буде перетинати невідсортований бін у напрямку bk до тих пір, поки не дійде до кінця (структура арени) з while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))

Крім того, кожного разу, коли розглядається новий чанк, виконуються деякі перевірки безпеки:

  • Якщо розмір чанку дивний (занадто малий або занадто великий): malloc(): invalid size (unsorted)

  • Якщо розмір наступного чанку дивний (занадто малий або занадто великий): malloc(): invalid next size (unsorted)

  • Якщо попередній розмір, вказаний наступним чанком, відрізняється від розміру чанку: malloc(): mismatching next->prev_size (unsorted)

  • Якщо не victim->bck->fd == victim або не victim->fd == av (арена): malloc(): unsorted double linked list corrupted

  • Оскільки ми завжди перевіряємо останній, його fd повинен завжди вказувати на структуру арени.

  • Якщо наступний чанк не вказує на те, що попередній використовується: malloc(): invalid next->prev_inuse (unsorted)

Якщо це було успішно, поверніть шматок, і все, якщо ні, продовжуйте виконувати функцію...

якщо рівний розмір

Продовжуйте видаляти шматок з біна, у випадку, якщо запитаний розмір точно відповідає розміру шматка:

  • Якщо tcache не заповнений, додайте його до tcache та продовжуйте вказувати, що є шматок tcache, який можна використовувати

  • Якщо tcache заповнений, просто використовуйте його, повертаючи його

// From https://github.com/bminor/glibc/blob/master/malloc/malloc.c#L4126C11-L4157C14

/* remove from unsorted list */
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);

/* Take now instead of binning if exact fit */

if (size == nb)
{
set_inuse_bit_at_offset (victim, size);
if (av != &main_arena)
set_non_main_arena (victim);
#if USE_TCACHE
/* Fill cache first, return to user only if cache fills.
We may return one of these chunks later.  */
if (tcache_nb > 0
&& tcache->counts[tc_idx] < mp_.tcache_count)
{
tcache_put (victim, tc_idx);
return_cached = 1;
continue;
}
else
{
#endif
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
#if USE_TCACHE
}
#endif
}

Якщо блок не повернуто або не додано до tcache, продовжуйте з кодом...

помістіть блок у бін

Збережіть перевірений блок у малий бін або у великий бін в залежності від розміру блоку (зберігаючи великий бін належним чином організованим).

Виконуються перевірки безпеки, щоб переконатися, що обидва подвійні зв'язані списки великого біна пошкоджені:

  • Якщо fwd->bk_nextsize->fd_nextsize != fwd: malloc(): великий бін подвійний зв'язаний список пошкоджений (nextsize)

  • Якщо fwd->bk->fd != fwd: malloc(): великий бін подвійний зв'язаний список пошкоджений (bk)

_int_malloc помістіть блок у бін

```c /* place chunk in bin */

if (in_smallbin_range (size)) { victim_index = smallbin_index (size); bck = bin_at (av, victim_index); fwd = bck->fd; } else { victim_index = largebin_index (size); bck = bin_at (av, victim_index); fwd = bck->fd;

/* maintain large bins in sorted order / if (fwd != bck) { / Or with inuse bit to speed comparisons / size |= PREV_INUSE; / if smaller than smallest, bypass loop below */ assert (chunk_main_arena (bck->bk)); if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)) { fwd = bck; bck = bck->bk;

victim->fd_nextsize = fwd->fd; victim->bk_nextsize = fwd->fd->bk_nextsize; fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; } else { assert (chunk_main_arena (fwd)); while ((unsigned long) size < chunksize_nomask (fwd)) { fwd = fwd->fd_nextsize; assert (chunk_main_arena (fwd)); }

if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd)) /* Always insert in the second position. */ fwd = fwd->fd; else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)"); fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk; if (bck->fd != fwd) malloc_printerr ("malloc(): largebin double linked list corrupted (bk)"); } } else victim->fd_nextsize = victim->bk_nextsize = victim; }

mark_bin (av, victim_index); victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;

#### Обмеження `_int_malloc`

На цьому етапі, якщо деякий фрагмент було збережено в tcache і його можна використовувати, а обмеження досягнуто, просто **поверніть фрагмент tcache**.

Крім того, якщо досягнуто **MAX\_ITERS**, вийдіть з циклу та отримайте фрагмент іншим способом (верхній фрагмент).

Якщо `return_cached` було встановлено, просто поверніть фрагмент з tcache, щоб уникнути великих пошуків.
```c
// From https://github.com/bminor/glibc/blob/master/malloc/malloc.c#L4227C1-L4250C7

#if USE_TCACHE
/* If we've processed as many chunks as we're allowed while
filling the cache, return one of the cached ones.  */
++tcache_unsorted_count;
if (return_cached
&& mp_.tcache_unsorted_limit > 0
&& tcache_unsorted_count > mp_.tcache_unsorted_limit)
{
return tcache_get (tc_idx);
}
#endif

#define MAX_ITERS       10000
if (++iters >= MAX_ITERS)
break;
}

#if USE_TCACHE
/* If all the small chunks we found ended up cached, return one now.  */
if (return_cached)
{
return tcache_get (tc_idx);
}
#endif

Якщо ліміт не досягнуто, продовжуйте з кодом...

Великий бін (за індексом)

Якщо запит великий (не в малий бін) і ми ще не повертали жодного чанка, отримуємо індекс запитаного розміру в великому біні, перевіряємо, чи не порожній він або чи найбільший чанк в цьому біні більший, ніж запитаний розмір, і в такому випадку знаходимо найменший чанк, який можна використати для запитаного розміру.

Якщо залишковий простір від кінцевого використаного чанка може бути новим чанком, додайте його до неупорядкованого біну, і оновлюється останній залишок.

При додаванні залишку до неупорядкованого біну виконується перевірка безпеки:

  • bck->fd-> bk != bck: malloc(): corrupted unsorted chunks

_int_malloc Великий бін (за індексом)

```c // From https://github.com/bminor/glibc/blob/master/malloc/malloc.c#L4252C7-L4317C10

/* If a large request, scan through the chunks of current bin in sorted order to find smallest that fits. Use the skip list for this. */

if (!in_smallbin_range (nb)) { bin = bin_at (av, idx);

/* skip scan if empty or largest chunk is too small */ if ((victim = first (bin)) != bin && (unsigned long) chunksize_nomask (victim)

= (unsigned long) (nb)) { victim = victim->bk_nextsize; while (((unsigned long) (size = chunksize (victim)) < (unsigned long) (nb))) victim = victim->bk_nextsize;

/* Avoid removing the first entry for a size so that the skip list does not have to be rerouted. */ if (victim != last (bin) && chunksize_nomask (victim) == chunksize_nomask (victim->fd)) victim = victim->fd;

remainder_size = size - nb; unlink_chunk (av, victim);

/* Exhaust / if (remainder_size < MINSIZE) { set_inuse_bit_at_offset (victim, size); if (av != &main_arena) set_non_main_arena (victim); } / Split / else { remainder = chunk_at_offset (victim, nb); / We cannot assume the unsorted list is empty and therefore have to perform a complete insert here. */ bck = unsorted_chunks (av); fwd = bck->fd; if (__glibc_unlikely (fwd->bk != bck)) malloc_printerr ("malloc(): corrupted unsorted chunks"); last_re->bk = bck; remainder->fd = fwd; bck->fd = remainder; fwd->bk = remainder; if (!in_smallbin_range (remainder_size)) { remainder->fd_nextsize = NULL; remainder->bk_nextsize = NULL; } set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0)); set_head (remainder, remainder_size | PREV_INUSE); set_foot (remainder, remainder_size); } check_malloced_chunk (av, victim, nb); void *p = chunk2mem (victim); alloc_perturb (p, bytes); return p; } }

</details>

Якщо підходящий блок не знайдено, продовжуйте

### Великий бін (наступний більший)

Якщо в точному великому біні не було жодного блоку, який можна було б використати, почніть перебирати всі наступні великі біни (починаючи з найбільшого) до тих пір, поки не буде знайдено один (якщо такий є).

Нагадування про розділений блок додається в неупорядкований бін, last\_reminder оновлюється, і виконується та сама перевірка безпеки:

* `bck->fd-> bk != bck`: `malloc(): corrupted unsorted chunks2`

<details>

<summary><code>_int_malloc</code> Великий бін (наступний більший)</summary>
```c
// From https://github.com/bminor/glibc/blob/master/malloc/malloc.c#L4319C7-L4425C10

/*
Search for a chunk by scanning bins, starting with next largest
bin. This search is strictly by best-fit; i.e., the smallest
(with ties going to approximately the least recently used) chunk
that fits is selected.

The bitmap avoids needing to check that most blocks are nonempty.
The particular case of skipping all bins during warm-up phases
when no chunks have been returned yet is faster than it might look.
*/

++idx;
bin = bin_at (av, idx);
block = idx2block (idx);
map = av->binmap[block];
bit = idx2bit (idx);

for (;; )
{
/* Skip rest of block if there are no more set bits in this block.  */
if (bit > map || bit == 0)
{
do
{
if (++block >= BINMAPSIZE) /* out of bins */
goto use_top;
}
while ((map = av->binmap[block]) == 0);

bin = bin_at (av, (block << BINMAPSHIFT));
bit = 1;
}

/* Advance to bin with set bit. There must be one. */
while ((bit & map) == 0)
{
bin = next_bin (bin);
bit <<= 1;
assert (bit != 0);
}

/* Inspect the bin. It is likely to be non-empty */
victim = last (bin);

/*  If a false alarm (empty bin), clear the bit. */
if (victim == bin)
{
av->binmap[block] = map &= ~bit; /* Write through */
bin = next_bin (bin);
bit <<= 1;
}

else
{
size = chunksize (victim);

/*  We know the first chunk in this bin is big enough to use. */
assert ((unsigned long) (size) >= (unsigned long) (nb));

remainder_size = size - nb;

/* unlink */
unlink_chunk (av, victim);

/* Exhaust */
if (remainder_size < MINSIZE)
{
set_inuse_bit_at_offset (victim, size);
if (av != &main_arena)
set_non_main_arena (victim);
}

/* Split */
else
{
remainder = chunk_at_offset (victim, nb);

/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here.  */
bck = unsorted_chunks (av);
fwd = bck->fd;
if (__glibc_unlikely (fwd->bk != bck))
malloc_printerr ("malloc(): corrupted unsorted chunks 2");
remainder->bk = bck;
remainder->fd = fwd;
bck->fd = remainder;
fwd->bk = remainder;

/* advertise as last remainder */
if (in_smallbin_range (nb))
av->last_remainder = remainder;
if (!in_smallbin_range (remainder_size))
{
remainder->fd_nextsize = NULL;
remainder->bk_nextsize = NULL;
}
set_head (victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
set_foot (remainder, remainder_size);
}
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}

Верхній чанк

На цьому етапі настав час отримати новий чанк з Верхнього чанку (якщо він достатньо великий).

Це починається з перевірки безпеки, щоб переконатися, що розмір чанку не є занадто великим (пошкодженим):

  • chunksize(av->top) > av->system_mem: malloc(): corrupted top size

Потім він використовує простір верхнього чанку, якщо він достатньо великий, щоб створити чанк потрібного розміру. Якщо ні, якщо є швидкі чанки, їх консолідують і спробують ще раз. Нарешті, якщо місця недостатньо, використовується sysmalloc для виділення достатнього розміру.

_int_malloc Верхній чанк

```c use_top: /* If large enough, split off the chunk bordering the end of memory (held in av->top). Note that this is in accord with the best-fit search rule. In effect, av->top is treated as larger (and thus less well fitting) than any other available chunk since it can be extended to be as large as necessary (up to system limitations).

We require that av->top always exists (i.e., has size >= MINSIZE) after initialization, so if it would otherwise be exhausted by current request, it is replenished. (The main reason for ensuring it exists is that we may need MINSIZE space to put in fenceposts in sysmalloc.) */

victim = av->top; size = chunksize (victim);

if (__glibc_unlikely (size > av->system_mem)) malloc_printerr ("malloc(): corrupted top size");

if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) { remainder_size = size - nb; remainder = chunk_at_offset (victim, nb); av->top = remainder; set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0)); set_head (remainder, remainder_size | PREV_INUSE);

check_malloced_chunk (av, victim, nb); void *p = chunk2mem (victim); alloc_perturb (p, bytes); return p; }

/* When we are using atomic ops to free fast chunks we can get here for all block sizes. / else if (atomic_load_relaxed (&av->have_fastchunks)) { malloc_consolidate (av); / restore original bin index */ if (in_smallbin_range (nb)) idx = smallbin_index (nb); else idx = largebin_index (nb); }

/* Otherwise, relay to handle system-dependent cases */ else { void *p = sysmalloc (nb, av); if (p != NULL) alloc_perturb (p, bytes); return p; } } }

### sysmalloc

### Початок sysmalloc

Якщо арена є нульовою або запитаний розмір є занадто великим (і є дозволені mmaps), використовуйте `sysmalloc_mmap` для виділення простору та повернення його.
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L2531

/*
sysmalloc handles malloc cases requiring more memory from the system.
On entry, it is assumed that av->top does not have enough
space to service request for nb bytes, thus requiring that av->top
be extended or replaced.
*/

static void *
sysmalloc (INTERNAL_SIZE_T nb, mstate av)
{
mchunkptr old_top;              /* incoming value of av->top */
INTERNAL_SIZE_T old_size;       /* its size */
char *old_end;                  /* its end address */

long size;                      /* arg to first MORECORE or mmap call */
char *brk;                      /* return value from MORECORE */

long correction;                /* arg to 2nd MORECORE call */
char *snd_brk;                  /* 2nd return val */

INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of new space */
INTERNAL_SIZE_T end_misalign;   /* partial page left at end of new space */
char *aligned_brk;              /* aligned offset into brk */

mchunkptr p;                    /* the allocated/returned chunk */
mchunkptr remainder;            /* remainder from allocation */
unsigned long remainder_size;   /* its size */


size_t pagesize = GLRO (dl_pagesize);
bool tried_mmap = false;


/*
If have mmap, and the request size meets the mmap threshold, and
the system supports mmap, and there are few enough currently
allocated mmapped regions, try to directly map this request
rather than expanding top.
*/

if (av == NULL
|| ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
&& (mp_.n_mmaps < mp_.n_mmaps_max)))
{
char *mm;
if (mp_.hp_pagesize > 0 && nb >= mp_.hp_pagesize)
{
/* There is no need to issue the THP madvise call if Huge Pages are
used directly.  */
mm = sysmalloc_mmap (nb, mp_.hp_pagesize, mp_.hp_flags, av);
if (mm != MAP_FAILED)
return mm;
}
mm = sysmalloc_mmap (nb, pagesize, 0, av);
if (mm != MAP_FAILED)
return mm;
tried_mmap = true;
}

/* There are no usable arenas and mmap also failed.  */
if (av == NULL)
return 0;

Перевірки sysmalloc

Це починається з отримання інформації про старий верхній фрагмент та перевірки того, що деякі з наступних умов є вірними:

  • Розмір старого купи нульовий (нова купа)

  • Розмір попередньої купи більший за MINSIZE, а старий Верх використовується

  • Купа вирівняна за розміром сторінки (0x1000, тому нижні 12 бітів повинні бути нульовими)

Потім також перевіряється, що:

  • Старий розмір не має достатньо місця для створення фрагмента потрібного розміру

Перевірки sysmalloc

```c /* Record incoming configuration of top */

old_top = av->top; old_size = chunksize (old_top); old_end = (char *) (chunk_at_offset (old_top, old_size));

brk = snd_brk = (char *) (MORECORE_FAILURE);

/* If not the first time through, we require old_size to be at least MINSIZE and to have prev_inuse set. */

assert ((old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0));

/* Precondition: not enough current space to satisfy nb request */ assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

### sysmalloc не головна арена

Спочатку він спробує **розширити** попередню купу для цієї купи. Якщо це неможливо, спробуйте **виділити нову купу** та оновіть вказівники, щоб мати змогу її використовувати.\
Нарешті, якщо це не спрацювало, спробуйте викликати **`sysmalloc_mmap`**.&#x20;

</details>
```c
if (av != &main_arena)
{
heap_info *old_heap, *heap;
size_t old_heap_size;

/* First try to extend the current heap. */
old_heap = heap_for_ptr (old_top);
old_heap_size = old_heap->size;
if ((long) (MINSIZE + nb - old_size) > 0
&& grow_heap (old_heap, MINSIZE + nb - old_size) == 0)
{
av->system_mem += old_heap->size - old_heap_size;
set_head (old_top, (((char *) old_heap + old_heap->size) - (char *) old_top)
| PREV_INUSE);
}
else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad)))
{
/* Use a newly allocated heap.  */
heap->ar_ptr = av;
heap->prev = old_heap;
av->system_mem += heap->size;
/* Set up the new top.  */
top (av) = chunk_at_offset (heap, sizeof (*heap));
set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE);

/* Setup fencepost and free the old top chunk with a multiple of
MALLOC_ALIGNMENT in size. */
/* The fencepost takes at least MINSIZE bytes, because it might
become the top chunk again later.  Note that a footer is set
up, too, although the chunk is marked in use. */
old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK;
set_head (chunk_at_offset (old_top, old_size + CHUNK_HDR_SZ),
0 | PREV_INUSE);
if (old_size >= MINSIZE)
{
set_head (chunk_at_offset (old_top, old_size),
CHUNK_HDR_SZ | PREV_INUSE);
set_foot (chunk_at_offset (old_top, old_size), CHUNK_HDR_SZ);
set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);
_int_free (av, old_top, 1);
}
else
{
set_head (old_top, (old_size + CHUNK_HDR_SZ) | PREV_INUSE);
set_foot (old_top, (old_size + CHUNK_HDR_SZ));
}
}
else if (!tried_mmap)
{
/* We can at least try to use to mmap memory.  If new_heap fails
it is unlikely that trying to allocate huge pages will
succeed.  */
char *mm = sysmalloc_mmap (nb, pagesize, 0, av);
if (mm != MAP_FAILED)
return mm;
}
}

sysmalloc головна арена

Він починає обчислювати необхідний обсяг пам'яті. Він почне запитувати послідовну пам'ять, щоб у цьому випадку можна було використовувати стару пам'ять, яка не використовується. Також виконуються деякі операції вирівнювання.

// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L2665C1-L2713C10

else     /* av == main_arena */


{ /* Request enough space for nb + pad + overhead */
size = nb + mp_.top_pad + MINSIZE;

/*
If contiguous, we can subtract out existing space that we hope to
combine with new space. We add it back later only if
we don't actually get contiguous space.
*/

if (contiguous (av))
size -= old_size;

/*
Round to a multiple of page size or huge page size.
If MORECORE is not contiguous, this ensures that we only call it
with whole-page arguments.  And if MORECORE is contiguous and
this is not first time through, this preserves page-alignment of
previous calls. Otherwise, we correct to page-align below.
*/

#ifdef MADV_HUGEPAGE
/* Defined in brk.c.  */
extern void *__curbrk;
if (__glibc_unlikely (mp_.thp_pagesize != 0))
{
uintptr_t top = ALIGN_UP ((uintptr_t) __curbrk + size,
mp_.thp_pagesize);
size = top - (uintptr_t) __curbrk;
}
else
#endif
size = ALIGN_UP (size, GLRO(dl_pagesize));

/*
Don't try to call MORECORE if argument is so big as to appear
negative. Note that since mmap takes size_t arg, it may succeed
below even if we cannot call MORECORE.
*/

if (size > 0)
{
brk = (char *) (MORECORE (size));
if (brk != (char *) (MORECORE_FAILURE))
madvise_thp (brk, size);
LIBC_PROBE (memory_sbrk_more, 2, brk, size);
}

sysmalloc головна арена попередня помилка 1

Якщо попередній повернув MORECORE_FAILURE, спробуйте ще раз виділити пам'ять, використовуючи sysmalloc_mmap_fallback

// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L2715C7-L2740C10