malloc & sysmalloc

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support 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 Bin

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

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

  • Якщо шматок неправильно вирівняний: malloc(): unaligned fastbin chunk detected 2

  • Якщо наступний шматок неправильно вирівняний: malloc(): unaligned fastbin chunk detected

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

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

malloc_consolidate

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

Невпорядкований бін

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

Початок

Це починається з великого циклу for, який буде проходити через невпорядкований бін у напрямку 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 заповнений, просто використайте його, повертаючи його

_int_malloc обмеження

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

Більше того, якщо досягнуто MAX_ITERS, вийдіть з циклу і отримайте шматок іншим способом (top chunk).

Якщо return_cached було встановлено, просто поверніть шматок з tcache, щоб уникнути більших пошуків.

Якщо шматок не знайдено підходящим для цього, продовжуйте

Велика корзина (наступна більша)

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

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

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

sysmalloc

sysmalloc початок

Якщо арена є нульовою або запитуваний розмір занадто великий (і залишилися дозволені mmaps), використовуйте sysmalloc_mmap для виділення пам'яті та повернення її.

sysmalloc не основна арена

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

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

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

sysmalloc finale

Завершіть виділення, оновивши інформацію про арену

// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L2921C3-L2943C12

if ((unsigned long) av->system_mem > (unsigned long) (av->max_system_mem))
av->max_system_mem = av->system_mem;
check_malloc_state (av);

/* finally, do the allocation */
p = av->top;
size = chunksize (p);

/* check that one of the above allocation paths succeeded */
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
{
remainder_size = size - nb;
remainder = chunk_at_offset (p, nb);
av->top = remainder;
set_head (p, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
check_malloced_chunk (av, p, nb);
return chunk2mem (p);
}

/* catch all failure paths */
__set_errno (ENOMEM);
return 0;

sysmalloc_mmap

Last updated