GNU Libc

LIBC -> Stands for GNU C library, it is an open source library that takes the form of a shared object. Alternate libc implementations also exist such as musl.It provides all the C functions such as printf, puts, etc. LIBC provides code for important functions of the operating system. It is so important that the system won’t work without it.

Stack vs Heap

In case of stack, the size of memory to be allocated is known to the compiler and whenever a function is called, its variables get their place on the stack. When the call finishes, the memory for those variables (and some other stuff related to the function) is deallocated. Here, memory management is in the hands of compiler and the developer doesn’t need to worry at all about it.

In case of heap, memory is allocated at runtime. This is also known as dynamic memory allocation.


malloc is a dynamic memory allocator. Used when a program can’t decide the size or number of objects needed by it at runtime. malloc allocates memory chunks which come from large, contiguous region of memory known as heap. Memory allocation is usually done via the mmap and brk system calls which is a little bit complicated for regular users. malloc makes the task of memory allocation a piece of cake. It handles the mmap and brk system calls internally, to request memory from the kernel and thus, manages everything! is a great post if you wanna read more about mmap and brk.

malloc uses mmap to create a private anonymous mapping segment. The primary purpose of private anonymous mapping is to allocate new memory (zero filled) and this new memory would be exclusively used by the calling process.

There are various implementations of malloc such as dlmalloc, ptmalloc2, jemalloc, etc. ptmalloc2, which is a fork of dlmalloc is used by the glibc. It is so widely used that it’s a great target for exploitation. It takes the size as an argument and returns a pointer to a memory chunk of slightly larger or same size.

vmmap command can be used in gdb to print the memory map of a process. mp_.sbrk_base can be used to view the heap

vis command in pwndbg is widely used to view the allocated memory chunks.

malloc returns a pointer to the allocated memory.

void *malloc(size_t size)

The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory. The memory is set to zero. If nmemb or size is 0, then calloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().

void *calloc(size_t nmemb, size_t size)

malloc and calloc create memory chunks, with size depending on the function arguments provided by the user when calling them.

Size of a chunk increases in multiple of 16 bytes. For example, malloc(0x18) creates a chunk of size 0x20 bytes, malloc(0x26) creates a chunk of size 0x30 bytes and so on.

Structure of a chunk

struct malloc_chunk {

  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;

typedef struct malloc_chunk* mchunkptr;

Top chunk

Based on the output of the vmmap command, we can see that the size of the heap is way larger than the size of some allocated chunk. Malloc treats the remaining unused memory as a single, large chunk known as the top chunk. As the number of allocations increases, the size of top chunk decreses.


The free function deallocates the previous allocated memory


Here, ptr is a pointer to a chunk.


Arenas are structures that contain references to one or more heaps. Head of free lists (bins) are also stored in an arena. A single arena can administrate multiple heaps and a new arena is created with an initial heap whenever a thread uses malloc for the very first time, upto a limit. The main thread gets a special arena known as the main arena, which resides in the .data section of the libc.


A bin is a linked list of free chunks. On the basis of chunk size, bins are of four types: fastbin, unsorted bin, small bin and large bin. There is one more type of bin known as tcache bin which was introduced in libc2.26 for performance optimizations.


Fastbins are just a small collection of singly linked non-circular linked lists that hold free chunks of specific sizes, also known as fast chunks. Fastbins can be seen in pwndbg by using the command fastbins. Whenever a chunk, that falls in the fastbin range gets freed, its address is written to the head of the appropriate fastbin in the heap’s arena. Fastbins work on the LIFO principle (Last in, first out). There are 10 fastbins, varying from size 0x20 to 0xb0. The 0x20 fastbin would only store chunks of size 0x20, a 0x30 fastbin would only store chunks of size 0x30. Note: only 7 fastbins (0x20-0x80) are available under default conditions. This default setting can be changed by calling the mallopt() function to change the global_max_fast variable. The first quadword of a free chunk stored in a fastbin represents the fd or forward pointer (pointing to the next element in the free list).


global_max_fast is a global variable that holds the size of the largest fastbin. In glibc, the size range of fastbins is (0x20 to 0xb0). Under default conditions only 7 out of 10 fastbins are available which sets the default size range as (0x20 to 0x80). Changing the value of global_max_fast allows us to use more fastbins.


There are 62 smallbins. Smallbins are a collection of double linked circular lists whose size range varies from 0x20 to 0x400. THey follow the FIFO principle (first in, first out).

Unsorted bin

There is only one unsorted bin. It is a circular double linked list which acts as a cache layer to speed up allocation and dealloaction requests. When a chunk , whose size doesn’t fall in the fastbin size range is freed, t