(No se explican todas las comprobaciones en este resumen y algunos casos se han omitido por brevedad)
__libc_malloc intenta obtener un fragmento de la tcache, si no lo consigue llama a _int_malloc
_int_malloc:
Intenta generar el área si no existe ninguna
Si hay algún fragmento de fast bin del tamaño correcto, lo utiliza
Llena la tcache con otros fragmentos rápidos
Si hay algún fragmento de small bin del tamaño correcto, lo utiliza
Llena la tcache con otros fragmentos de ese tamaño
Si el tamaño solicitado no es para small bins, consolida el fast bin en unsorted bin
Comprueba el unsorted bin, utiliza el primer fragmento con suficiente espacio
Si el fragmento encontrado es más grande, divídelo para devolver una parte y añadir el resto de nuevo al unsorted bin
Si un fragmento es del mismo tamaño que el solicitado, úsalo para llenar la tcache en lugar de devolverlo (hasta que la tcache esté llena, luego devuelve el siguiente)
Por cada fragmento de tamaño más pequeño comprobado, colócalo en su respectivo small o large bin
Comprueba el large bin en el índice del tamaño solicitado
Comienza a buscar desde el primer fragmento que sea más grande que el tamaño solicitado, si se encuentra alguno, devuélvelo y añade los restos al small bin
Comprueba los large bins desde los índices siguientes hasta el final
Desde el siguiente índice más grande, busca cualquier fragmento, divide el primer fragmento encontrado para usarlo en el tamaño solicitado y añade el resto al unsorted bin
Si no se encuentra nada en los bins anteriores, obtén un fragmento del chunk superior
Si el chunk superior no era lo suficientemente grande, agrándalo con sysmalloc
__libc_malloc
La función malloc en realidad llama a __libc_malloc. Esta función comprobará la tcache para ver si hay algún fragmento disponible del tamaño deseado. Si lo hay, lo utilizará y si no, comprobará si es un solo hilo y en ese caso llamará a _int_malloc en la arena principal, y si no, llamará a _int_malloc en la arena del hilo.
Código de __libc_malloc
```c // From https://github.com/bminor/glibc/blob/master/malloc/malloc.c
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);
</details>
Observa cómo siempre etiquetará el puntero devuelto con `tag_new_usable`, según el código:
```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
Esta es la función que asigna memoria utilizando los otros bins y el fragmento superior.
Comienzo
Comienza definiendo algunas variables y obteniendo el tamaño real que necesita tener el espacio de memoria solicitado:
Fast Bin
Si el tamaño necesario está dentro de los tamaños de Fast Bins, intenta usar un fragmento de la fast bin. Básicamente, basado en el tamaño, encontrará el índice de fast bin donde deberían estar ubicados los fragmentos válidos, y si hay alguno, devolverá uno de esos.
Además, si tcache está habilitado, llenará la tcache bin de ese tamaño con fast bins.
Mientras se realizan estas acciones, se ejecutan algunas comprobaciones de seguridad aquí:
Si el fragmento no está alineado: malloc(): fragmento fastbin no alineado detectado 2
Si el fragmento siguiente no está alineado: malloc(): fragmento fastbin no alineado detectado
Si el fragmento devuelto tiene un tamaño incorrecto debido a su índice en la fast bin: malloc(): corrupción de memoria (fast)
Si algún fragmento utilizado para llenar la tcache no está alineado: malloc(): fragmento fastbin no alineado detectado 3
malloc_consolidate
Si no era un fragmento pequeño, es un fragmento grande, y en este caso se llama a malloc_consolidate para evitar la fragmentación de memoria.
Bin desordenado
Es hora de revisar el bin desordenado en busca de un fragmento válido para usar.
Inicio
Esto comienza con un gran bucle que recorrerá el bin desordenado en la dirección bk hasta que llegue al final (la estructura de arena) con while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
Además, se realizan algunas comprobaciones de seguridad cada vez que se considera un nuevo fragmento:
Si el tamaño del fragmento es extraño (demasiado pequeño o demasiado grande): malloc(): tamaño no válido (desordenado)
Si el tamaño del fragmento siguiente es extraño (demasiado pequeño o demasiado grande): malloc(): tamaño siguiente no válido (desordenado)
Si el tamaño previo indicado por el fragmento siguiente difiere del tamaño del fragmento: malloc(): tamaño previo no coincidente (desordenado)
Si no victim->bck->fd == victim o no victim->fd == av (arena): malloc(): lista doble enlazada desordenada
Como siempre estamos revisando el último, su fd debería apuntar siempre a la estructura de arena.
Si el fragmento siguiente no indica que el anterior está en uso: malloc(): previo no válido->prev_inuse (desordenado)
Si esto fue exitoso, devuelve el fragmento y se acabó, si no, continúa ejecutando la función...
si es de igual tamaño
Continúa eliminando el fragmento del bin, en caso de que el tamaño solicitado sea exactamente el del fragmento:
Si el tcache no está lleno, agréguelo al tcache y continúe indicando que hay un fragmento de tcache que podría ser utilizado
Si el tcache está lleno, simplemente úsalo devolviéndolo
Límites de _int_malloc
En este punto, si algún fragmento estaba almacenado en la tcache y se puede usar y se alcanza el límite, simplemente devuelve un fragmento de la tcache.
Además, si se alcanza MAX_ITERS, sal del bucle y obtén un fragmento de una manera diferente (fragmento superior).
Si return_cached estaba configurado, simplemente devuelve un fragmento de la tcache para evitar búsquedas más largas.
Si no se encuentra un fragmento adecuado para esto, continúa
Contenedor grande (siguiente más grande)
Si en el contenedor grande exacto no hay ningún fragmento que se pueda usar, comienza a recorrer todos los contenedores grandes siguientes (comenzando por el inmediatamente más grande) hasta que se encuentre uno (si hay alguno).
El recordatorio del fragmento dividido se agrega en el contenedor no ordenado, last_reminder se actualiza y se realiza la misma verificación de seguridad:
bck->fd-> bk != bck: malloc(): corrupted unsorted chunks2
sysmalloc
Inicio de sysmalloc
Si la arena es nula o el tamaño solicitado es demasiado grande (y aún quedan mmaps permitidos), utiliza sysmalloc_mmap para asignar espacio y devolverlo.
// 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 enoughspace to service request for nb bytes, thus requiring that av->topbe extended or replaced.*/staticvoid*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 */unsignedlong 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, andthe system supports mmap, and there are few enough currentlyallocated mmapped regions, try to directly map this requestrather than expanding top.*/if (av ==NULL|| ((unsignedlong) (nb) >= (unsignedlong) (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 areused 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)return0;
Verificaciones de sysmalloc
Comienza obteniendo información del fragmento superior antiguo y verificando que algunas de las siguientes condiciones sean verdaderas:
El tamaño del montón antiguo es 0 (nuevo montón)
El tamaño del montón anterior es mayor que MINSIZE y el fragmento superior antiguo está en uso
El montón está alineado al tamaño de página (0x1000, por lo que los 12 bits inferiores deben ser 0)
Luego también verifica que:
El tamaño antiguo no tiene suficiente espacio para crear un fragmento del tamaño solicitado
/* Precondition: not enough current space to satisfy nb request */ assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));
</details>
### sysmalloc no en la arena principal
Primero intentará **extender** el montón anterior para este montón. Si no es posible, intentará **asignar un nuevo montón** y actualizar los punteros para poder usarlo.\
Finalmente, si eso no funcionó, intentará llamar a **`sysmalloc_mmap`**. 
<details>
<summary>sysmalloc no en la arena principal</summary>
```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 main arena
Comienza calculando la cantidad de memoria necesaria. Comenzará solicitando memoria contigua para poder utilizar la memoria antigua no utilizada. También se realizan algunas operaciones de alineación.
sysmalloc main arena
```c // 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. */
### Error anterior en la arena principal de `sysmalloc` 1
Si el error anterior devolvió `MORECORE_FAILURE`, intenta nuevamente asignar memoria usando `sysmalloc_mmap_fallback`
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L2715C7-L2740C10
if (brk == (char *) (MORECORE_FAILURE))
{
/*
If have mmap, try using it as a backup when MORECORE fails or
cannot be used. This is worth doing on systems that have "holes" in
address space, so sbrk cannot extend to give contiguous space, but
space is available elsewhere. Note that we ignore mmap max count
and threshold limits, since the space will not be used as a
segregated mmap region.
*/
char *mbrk = MAP_FAILED;
if (mp_.hp_pagesize > 0)
mbrk = sysmalloc_mmap_fallback (&size, nb, old_size,
mp_.hp_pagesize, mp_.hp_pagesize,
mp_.hp_flags, av);
if (mbrk == MAP_FAILED)
mbrk = sysmalloc_mmap_fallback (&size, nb, old_size, MMAP_AS_MORECORE_SIZE,
pagesize, 0, av);
if (mbrk != MAP_FAILED)
{
/* We do not need, and cannot use, another sbrk call to find end */
brk = mbrk;
snd_brk = brk + size;
}
}
Continuar con la arena principal de sysmalloc
Si lo anterior no devolvió MORECORE_FAILURE, si funcionó, crear algunos alineamientos:
Error anterior de la arena principal de sysmalloc 2
```c // From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L2742
if (brk != (char *) (MORECORE_FAILURE)) { if (mp_.sbrk_base == 0) mp_.sbrk_base = brk; av->system_mem += size;
/* If MORECORE extends previous space, we can likewise extend top size. */
If the first time through or noncontiguous, we need to call sbrk just to find out where the end of memory lies.
We need to ensure that all returned chunks from malloc will meet MALLOC_ALIGNMENT
If there was an intervening foreign sbrk, we need to adjust sbrk request size to account for fact that we will not be able to combine new space with existing space in old_top.
Almost all systems internally allocate whole pages at a time, in which case we might as well use the whole last page of request. So we allocate enough more memory to hit a page boundary now, which in turn causes future contiguous calls to page-align. */
/* handle contiguous cases / if (contiguous (av)) { / Count foreign sbrk as system_mem. */ if (old_size) av->system_mem += brk - old_end;
/* Guarantee alignment of first new chunk made from this space */
front_misalign = (INTERNAL_SIZE_T) chunk2mem (brk) & MALLOC_ALIGN_MASK; if (front_misalign > 0) { /* Skip over some bytes to arrive at an aligned position. We don't need to specially mark these wasted front bytes. They will never be accessed anyway because prev_inuse of av->top (and any chunk created from its start) is always true after initialization. */
/* If can't allocate correction, try to at least find out current brk. It might be enough to proceed without failing.
Note that if second sbrk did NOT fail, we assume that space is contiguous with first sbrk. This is a safe assumption unless program is multithreaded but doesn't use locks and a foreign sbrk occurred between our first and second calls. */
/* handle non-contiguous cases / else { if (MALLOC_ALIGNMENT == CHUNK_HDR_SZ) / MORECORE/mmap must correctly align / assert (((unsigned long) chunk2mem (brk) & MALLOC_ALIGN_MASK) == 0); else { front_misalign = (INTERNAL_SIZE_T) chunk2mem (brk) & MALLOC_ALIGN_MASK; if (front_misalign > 0) { / Skip over some bytes to arrive at an aligned position. We don't need to specially mark these wasted front bytes. They will never be accessed anyway because prev_inuse of av->top (and any chunk created from its start) is always true after initialization. */
/* Find out current end of memory */ if (snd_brk == (char *) (MORECORE_FAILURE)) { snd_brk = (char *) (MORECORE (0)); } }
/* Adjust top based on results of second sbrk */ if (snd_brk != (char *) (MORECORE_FAILURE)) { av->top = (mchunkptr) aligned_brk; set_head (av->top, (snd_brk - aligned_brk + correction) | PREV_INUSE); av->system_mem += correction;
/* If not the first time through, we either have a gap due to foreign sbrk or a non-contiguous region. Insert a double fencepost at old_top to prevent consolidation with space we don't own. These fenceposts are artificial chunks that are marked as inuse and are in any case too small to use. We need two to make sizes and alignments work out. */
if (old_size != 0) { /* Shrink old_top to insert fenceposts, keeping size a multiple of MALLOC_ALIGNMENT. We know there is at least enough space in old_top to do this. */ old_size = (old_size - 2 * CHUNK_HDR_SZ) & ~MALLOC_ALIGN_MASK; set_head (old_top, old_size | PREV_INUSE);
/* Note that the following assignments completely overwrite old_top when old_size was previously MINSIZE. This is intentional. We need the fencepost, even if old_top otherwise gets lost. */ set_head (chunk_at_offset (old_top, old_size), CHUNK_HDR_SZ | PREV_INUSE); set_head (chunk_at_offset (old_top, old_size + CHUNK_HDR_SZ), CHUNK_HDR_SZ | PREV_INUSE);
/* If possible, release the rest. / if (old_size >= MINSIZE) { _int_free (av, old_top, 1); } } } } } } / if (av != &main_arena) */
</details>
### sysmalloc final
Finaliza la asignación actualizando la información del área.
```c
// 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;