malloc & sysmalloc

рд╣реИрдХрдЯреНрд░рд┐рдХреНрд╕ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░реЗрдВ

рдЖрд╡рдВрдЯрди рдХреНрд░рдо рд╕рд╛рд░рд╛рдВрд╢

(рдЗрд╕ рд╕рд╛рд░рд╛рдВрд╢ рдореЗрдВ рдХреЛрдИ рдЬрд╛рдВрдЪ рдирд╣реАрдВ рдХреА рдЧрдИ рд╣реИ рдФрд░ рдХреБрдЫ рдорд╛рдорд▓реЗ рд╕рдВрдХреНрд╖реЗрдк рдХреЗ рд▓рд┐рдП рдЫреЛрдбрд╝ рджрд┐рдП рдЧрдП рд╣реИрдВ)

  1. __libc_malloc рдЯреАрдХреИрд╢ рд╕реЗ рдПрдХ рдЪрдВрдХ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рддрд╛ рд╣реИ, рдЕрдЧрд░ рдирд╣реАрдВ рддреЛ рд╡рд╣ _int_malloc рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ

  2. _int_malloc :

  3. рдПрд░реАрдирд╛ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рддрд╛ рд╣реИ рдЕрдЧрд░ рдХреЛрдИ рдирд╣реАрдВ рд╣реИ

  4. рдпрджрд┐ рдХреЛрдИ рд╕рд╣реА рдЖрдХрд╛рд░ рдХрд╛ рдлрд╛рд╕реНрдЯ рдмрд┐рди рдЪрдВрдХ рд╣реИ, рддреЛ рдЙрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ

  5. рдЕрдиреНрдп рдлрд╛рд╕реНрдЯ рдЪрдВрдХ рдХреЗ рд╕рд╛рде рдЯреАрдХреИрд╢ рднрд░реЗрдВ

  6. рдпрджрд┐ рдХреЛрдИ рдЫреЛрдЯрд╛ рдмрд┐рди рдЪрдВрдХ рд╕рд╣реА рдЖрдХрд╛рд░ рдХрд╛ рд╣реИ, рддреЛ рдЙрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ

  7. рдЙрд╕ рдЖрдХрд╛рд░ рдХреЗ рдЕрдиреНрдп рдЪрдВрдХ рдХреЗ рд╕рд╛рде рдЯреАрдХреИрд╢ рднрд░реЗрдВ

  8. рдпрджрд┐ рдЕрдиреБрд░реЛрдзрд┐рдд рдЖрдХрд╛рд░ рдЫреЛрдЯреЗ рдмрд┐рдиреЛрдВ рдХреЗ рд▓рд┐рдП рдирд╣реАрдВ рд╣реИ, рддреЛ рдлрд╛рд╕реНрдЯ рдмрд┐рди рдХреЛ рдЕрдирд╕реЙрд░реНрдЯреЗрдб рдмрд┐рди рдореЗрдВ рд╕рдореЗрдЯреЗрдВ

  9. рдЕрдирд╕реЙрд░реНрдЯреЗрдб рдмрд┐рди рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВ, рдкрд╣рд▓рд╛ рдЪрдВрдХ рдЙрдкрдпреБрдХреНрдд рд╕реНрдерд╛рди рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ

  10. рдпрджрд┐ рдкрд╛рдпрд╛ рдЧрдпрд╛ рдЪрдВрдХ рдмрдбрд╝рд╛ рд╣реИ, рддреЛ рдЙрд╕реЗ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рдХреЗ рдПрдХ рд╣рд┐рд╕реНрд╕рд╛ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рдФрд░ рдмрд╛рдХреА рдХреЛ рдЕрдирд╕реЙрд░реНрдЯреЗрдб рдмрд┐рди рдореЗрдВ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП

  11. рдпрджрд┐ рдПрдХ рдЪрдВрдХ рдЕрдиреБрд░реЛрдзрд┐рдд рдЖрдХрд╛рд░ рдХреЗ рд╕рдорд╛рди рд╣реИ, рддреЛ рдЗрд╕реЗ рд╡рд╛рдкрд╕ рджреЗрдиреЗ рдХреА рдмрдЬрд╛рдп рдЯреАрдХреИрд╢ рднрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ (рдЬрдм рддрдХ рдЯреАрдХреИрд╢ рднрд░рд╛ рдирд╣реАрдВ рд╣реЛрддрд╛, рддреЛ рдЕрдЧрд▓рд╛ рд╡рд╛рдкрд╕ рдХрд░реЗрдВ)

  12. рдЫреЛрдЯреЗ рдпрд╛ рдмрдбрд╝реЗ рдЖрдХрд╛рд░ рдХреЗ рдкреНрд░рддреНрдпреЗрдХ рдЪрдВрдХ рдХреЗ рд▓рд┐рдП рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдкрд░, рдЙрд╕реЗ рдЙрд╕рдХреЗ рд╕рдВрдмрдВрдзрд┐рдд рдЫреЛрдЯреЗ рдпрд╛ рдмрдбрд╝реЗ рдмрд┐рди рдореЗрдВ рдбрд╛рд▓реЗрдВ

  13. рдЕрдиреБрд░реЛрдзрд┐рдд рдЖрдХрд╛рд░ рдХреЗ рд╕реВрдЪрдХрд╛рдВрдХ рдореЗрдВ рдмрдбрд╝реЗ рдмрд┐рди рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВ

  14. рдЕрдиреБрд░реЛрдзрд┐рдд рдЖрдХрд╛рд░ рд╕реЗ рдмрдбрд╝рд╛ рдкрд╣рд▓рд╛ рдЪрдВрдХ рд╕реЗ рджреЗрдЦрдирд╛ рд╢реБрд░реВ рдХрд░реЗрдВ, рдпрджрд┐ рдХреЛрдИ рдорд┐рд▓рддрд╛ рд╣реИ рддреЛ рдЙрд╕реЗ рд╡рд╛рдкрд╕ рдХрд░реЗрдВ рдФрд░ рдмрд╛рдХреА рдХреЛ рдЫреЛрдЯреЗ рдмрд┐рди рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ

  15. рдЕрдВрдд рддрдХ рдмрдбрд╝реЗ рдмрд┐рди рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВ

  16. рдЕрдЧрд▓реЗ рдмрдбрд╝реЗ рд╕реВрдЪрдХрд╛рдВрдХ рд╕реЗ рдХрд┐рд╕реА рднреА рдЪрдВрдХ рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВ, рдкрд╣рд▓рд╛ рдкрд╛рдпрд╛ рдЧрдпрд╛ рдЪрдВрдХ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░реЗрдВ рдФрд░ рдмрд╛рдХреА рдХреЛ рдЕрдирд╕реЙрд░реНрдЯреЗрдб рдмрд┐рди рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ

  17. рдкрд┐рдЫрд▓реЗ рдмрд┐рдиреЛрдВ рдореЗрдВ рдХреБрдЫ рднреА рдирд╣реАрдВ рдорд┐рд▓рд╛ рд╣реЛ, рддреЛ рдКрдкрд░ рд╕реЗ рдПрдХ рдЪрдВрдХ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ

  18. рдпрджрд┐ рдКрдкрд░ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдЪрдВрдХ рдкрд░реНрдпрд╛рдкреНрдд рдмрдбрд╝рд╛ рдирд╣реАрдВ рдерд╛, рддреЛ sysmalloc рдХреЗ рд╕рд╛рде рдЗрд╕реЗ рдмрдбрд╝рд╛ рдХрд░реЗрдВ

__libc_malloc

malloc рдлрд╝рдВрдХреНрд╢рди рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ __libc_malloc рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИред рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдЯреАрдХреИрд╢ рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдЧрд╛ рдХрд┐ рдХреНрдпрд╛ рдЪрд╛рд╣рд┐рдП рдЧрдП рдЖрдХрд╛рд░ рдХрд╛ рдХреЛрдИ рдЙрдкрд▓рдмреНрдз рдЪрдВрдХ рд╣реИред рдпрджрд┐ рд╣рд╛рдВ, рддреЛ рдпрд╣ рдЙрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдЧрд╛ рдФрд░ рдпрджрд┐ рдирд╣реАрдВ, рддреЛ рдпрд╣ рджреЗрдЦреЗрдЧрд╛ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдПрдХрд▓ рдзрд╛рдЧрд╛ рд╣реИ рдФрд░ рдЙрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдпрд╣ рдореБрдЦреНрдп рдПрд░реАрдирд╛ рдореЗрдВ _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

рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдЕрдиреНрдп рдмрд┐рди рдФрд░ рд╢реАрд░реНрд╖ рдЪрдВрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдореЗрдореЛрд░реА рдХрд╛ рдЖрд╡рдВрдЯрди рдХрд░рддрд╛ рд╣реИред

  • рдкреНрд░рд╛рд░рдВрдн

рдпрд╣ рдХреБрдЫ рд╡реЗрд░рд┐рдПрдмрд▓реНрд╕ рдХреА рдкрд░рд┐рднрд╛рд╖рд╛ рд╢реБрд░реВ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЕрдиреБрд░реЛрдзрд┐рдд рдореЗрдореЛрд░реА рд╕реНрдерд╛рди рдХреА рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╕рд╛рдЗрдЬрд╝ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ:

// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L3847
static void *
_int_malloc (mstate av, size_t bytes)
{
INTERNAL_SIZE_T nb;               /* normalized request size */
unsigned int idx;                 /* associated bin index */
mbinptr bin;                      /* associated bin */

mchunkptr victim;                 /* inspected/selected chunk */
INTERNAL_SIZE_T size;             /* its size */
int victim_index;                 /* its bin index */

mchunkptr remainder;              /* remainder from a split */
unsigned long remainder_size;     /* its size */

unsigned int block;               /* bit map traverser */
unsigned int bit;                 /* bit map traverser */
unsigned int map;                 /* current word of binmap */

mchunkptr fwd;                    /* misc temp for linking */
mchunkptr bck;                    /* misc temp for linking */

#if USE_TCACHE
size_t tcache_unsorted_count;	    /* count of unsorted chunks processed */
#endif

/*
Convert request size to internal form by adding SIZE_SZ bytes
overhead plus possibly more to obtain necessary alignment and/or
to obtain a size of at least MINSIZE, the smallest allocatable
size. Also, checked_request2size returns false for request sizes
that are so large that they wrap around zero when padded and
aligned.
*/

nb = checked_request2size (bytes);
if (nb == 0)
{
__set_errno (ENOMEM);
return NULL;
}

рдХреНрд╖реЗрддреНрд░

рдпрджрд┐ рдЙрдкрдпреЛрдЧреА рдХреНрд╖реЗрддреНрд░ рдирд╣реАрдВ рд╣реИ, рддреЛ sysmalloc рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ mmap рд╕реЗ рдЯреБрдХрдбрд╝рд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ:

// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L3885C3-L3893C6
/* There are no usable arenas.  Fall back to sysmalloc to get a chunk from
mmap.  */
if (__glibc_unlikely (av == NULL))
{
void *p = sysmalloc (nb, av);
if (p != NULL)
alloc_perturb (p, bytes);
return p;
}

рддреНрд╡рд░рд┐рдд рдмрд┐рди

рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рдЖрдХрд╛рд░ рддреНрд╡рд░рд┐рдд рдмрд┐рди рдЖрдХрд╛рд░реЛрдВ рдХреЗ рдЕрдВрджрд░ рд╣реИ, рддреЛ рддреНрд╡рд░рд┐рдд рдмрд┐рди рд╕реЗ рдПрдХ рдЪрдВрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВред рдореВрд▓ рд░реВрдк рд╕реЗ, рдЖрдХрд╛рд░ рдХреЗ рдЖрдзрд╛рд░ рдкрд░, рдпрд╣ рд╡реИрдз рдЪрдВрдХ рдХрд╣рд╛рдБ рд╕реНрдерд┐рдд рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдП, рдЙрд╕ рддреНрд╡рд░рд┐рдд рдмрд┐рди рд╕реВрдЪрдХрд╛рдВрдХ рдХреЛ рдЦреЛрдЬреЗрдЧрд╛, рдФрд░ рдпрджрд┐ рдХреЛрдИ рд╣реЛ, рддреЛ рд╡рд╣ рдЙрдирдореЗрдВ рд╕реЗ рдПрдХ рд▓реМрдЯрд╛рдПрдЧрд╛ред рдЗрд╕рдХреЗ рдЕрддрд┐рд░рд┐рдХреНрдд, рдпрджрд┐ рдЯреАрдХреИрд╢ рдЪрд╛рд▓реВ рд╣реИ, рддреЛ рдпрд╣ рдЙрд╕ рдЖрдХрд╛рд░ рдХреЗ рдЯреАрдХреИрд╢ рдмрд┐рди рдХреЛ рддреНрд╡рд░рд┐рдд рдмрд┐рди рдХреЗ рд╕рд╛рде рднрд░реЗрдЧрд╛ред

рдЬрдм рдпреЗ рдХреНрд░рд┐рдпрд╛рдПрдБ рдХреА рдЬрд╛рддреА рд╣реИрдВ, рддреЛ рдпрд╣рд╛рдБ рдХреБрдЫ рд╕реБрд░рдХреНрд╖рд╛ рдЬрд╛рдВрдЪреЗрдВ рдХреА рдЬрд╛рддреА рд╣реИрдВ:

  • рдпрджрд┐ рдЪрдВрдХ рдЧрд▓рдд рд░реВрдк рд╕реЗ рд╕реНрдерд┐рдд рд╣реИ: malloc(): unaligned fastbin chunk detected 2

  • рдпрджрд┐ рдЖрдЧреЗ рдХрд╛ рдЪрдВрдХ рдЧрд▓рдд рд░реВрдк рд╕реЗ рд╕реНрдерд┐рдд рд╣реИ: malloc(): unaligned fastbin chunk detected

  • рдпрджрд┐ рд▓реМрдЯрд╛рдпрд╛ рдЧрдпрд╛ рдЪрдВрдХ рдЙрд╕ рдЖрдХрд╛рд░ рдХрд╛ рд╣реИ рдЬреЛ рддреНрд╡рд░рд┐рдд рдмрд┐рди рдореЗрдВ рдЙрд╕рдХреЗ рд╕реВрдЪрдХрд╛рдВрдХ рдХреЗ рдХрд╛рд░рдг рд╕рд╣реА рдирд╣реАрдВ рд╣реИ: malloc(): memory corruption (fast)

  • рдпрджрд┐ рдХреЛрдИ рднреА рдЪрдВрдХ рдЬреЛ рдЯреАрдХреИрд╢ рднрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдЧрд▓рдд рд░реВрдк рд╕реЗ рд╕реНрдерд┐рдд рд╣реИ: malloc(): unaligned fastbin chunk detected 3

_int_malloc fast bin

```c // From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L3895C3-L3967C6 /* If the size qualifies as a fastbin, first check corresponding bin. This code is safe to execute even if av is not yet initialized, so we can try it without checking, which saves some time on this fast path. */

#define REMOVE_FB(fb, victim, pp) do { victim = pp; if (victim == NULL) break; pp = REVEAL_PTR (victim->fd); if (__glibc_unlikely (pp != NULL && misaligned_chunk (pp))) malloc_printerr ("malloc(): unaligned fastbin chunk detected"); } while ((pp = catomic_compare_and_exchange_val_acq (fb, pp, victim)) != victim); \

if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ())) { idx = fastbin_index (nb); mfastbinptr *fb = &fastbin (av, idx); mchunkptr pp; victim = *fb;

if (victim != NULL) { if (__glibc_unlikely (misaligned_chunk (victim))) malloc_printerr ("malloc(): unaligned fastbin chunk detected 2");

if (SINGLE_THREAD_P) fb = REVEAL_PTR (victim->fd); else REMOVE_FB (fb, pp, victim); if (__glibc_likely (victim != NULL)) { size_t victim_idx = fastbin_index (chunksize (victim)); if (__builtin_expect (victim_idx != idx, 0)) malloc_printerr ("malloc(): memory corruption (fast)"); check_remalloced_chunk (av, victim, nb); #if USE_TCACHE / While we're here, if we see other chunks of the same size, stash them in the tcache. */ size_t tc_idx = csize2tidx (nb); if (tcache != NULL && tc_idx < mp_.tcache_bins) { mchunkptr tc_victim;

/* While bin not empty and tcache not full, copy chunks. */ while (tcache->counts[tc_idx] < mp_.tcache_count && (tc_victim = *fb) != NULL) { if (__glibc_unlikely (misaligned_chunk (tc_victim))) malloc_printerr ("malloc(): unaligned fastbin chunk detected 3"); if (SINGLE_THREAD_P) *fb = REVEAL_PTR (tc_victim->fd); else { REMOVE_FB (fb, pp, tc_victim); if (__glibc_unlikely (tc_victim == NULL)) break; } tcache_put (tc_victim, tc_idx); } } #endif void *p = chunk2mem (victim); alloc_perturb (p, bytes); return p; } } }

</details>

### рдЫреЛрдЯрд╛ рдмрд┐рди

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдореЗрдВ рдЗрдВрдбрд┐рдХреЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ рдХрд┐ рдЫреЛрдЯреЗ рдмрд┐рдиреНрд╕ рдкреНрд░рддрд┐ рдЗрдВрдбреЗрдХреНрд╕ рдПрдХ рд╕рд╛рдЗрдЬрд╝ рдзрд╛рд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдпрджрд┐ рдПрдХ рд╡реИрдз рдЪрдВрдХ рдЙрдкрд▓рдмреНрдз рд╣реИ рддреЛ рдЙрд╕реЗ рдЬрд╛рдВрдЪрдирд╛ рдмрд╣реБрдд рддреЗрдЬреА рд╕реЗ рд╣реЛрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдлрд╛рд╕реНрдЯ рдмрд┐рдиреНрд╕ рдХреЗ рдмрд╛рдж, рдЫреЛрдЯреЗ рдмрд┐рдиреНрд╕ рдХреА рдЬрд╛рдВрдЪ рдХреА рдЬрд╛рддреА рд╣реИред

рдкрд╣рд▓реА рдЬрд╛рдВрдЪ рдпрд╣ рд╣реИ рдХрд┐ рдХреНрдпрд╛ рдЕрдиреБрд░реЛрдзрд┐рдд рд╕рд╛рдЗрдЬрд╝ рдПрдХ рдЫреЛрдЯреЗ рдмрд┐рди рдореЗрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдЙрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЫреЛрдЯреЗ рдмрд┐рди рдХреЗ рдЕрдВрджрд░ рд╕рдВрдмрдВрдзрд┐рдд **рдЗрдВрдбреЗрдХреНрд╕** рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ рдФрд░ рджреЗрдЦреЗрдВ рдХрд┐ рдХреНрдпрд╛ **рдХреЛрдИ рдЙрдкрд▓рдмреНрдз рдЪрдВрдХ** рд╣реИред

рдлрд┐рд░, рдПрдХ рд╕реБрд░рдХреНрд╖рд╛ рдЬрд╛рдВрдЪ рдХреА рдЬрд╛рддреА рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╢рд╛рдорд┐рд▓ рд╣реИ:

* &#x20;рдпрджрд┐ `victim->bk->fd = victim`. рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдХреНрдпрд╛ рджреЛрдиреЛрдВ рдЪрдВрдХ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рд▓рд┐рдВрдХ рд╣реИрдВред

рдЙрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЪрдВрдХ **рдХреЛ `inuse` рдмрд┐рдЯ рдорд┐рд▓рддрд╛ рд╣реИ,** рдбрдмрд▓ рд▓рд┐рдВрдХреНрдб рд╕реВрдЪреА рдареАрдХ рд╣реЛ рдЬрд╛рддреА рд╣реИ рддрд╛рдХрд┐ рдпрд╣ рдЪрдВрдХ рдЙрд╕рд╕реЗ рдЧрд╛рдпрдм рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ (рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛), рдФрд░ рдЧреИрд░ рдореБрдЦреНрдп рдПрд░рд┐рдирд╛ рдмрд┐рдЯ рд╕реЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рд╣реЛред

рдЕрдВрддрддрдГ, **рдЕрдиреБрд░реЛрдзрд┐рдд рд╕рд╛рдЗрдЬрд╝ рдХреЗ tcache рдЗрдВрдбреЗрдХреНрд╕ рдХреЛ** рдЫреЛрдЯреЗ рдмрд┐рди рдХреЗ рдЕрдВрджрд░ рдЕрдиреНрдп рдЪрдВрдХреНрд╕ рд╕реЗ рднрд░реЗрдВ (рдпрджрд┐ рдХреЛрдИ рд╣реЛ)ред

<details>

<summary>_int_malloc small bin</summary>
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L3895C3-L3967C6

/*
If a small request, check regular bin.  Since these "smallbins"
hold one size each, no searching within bins is necessary.
(For a large request, we need to wait until unsorted chunks are
processed to find best fit. But for small ones, fits are exact
anyway, so we can check now, which is faster.)
*/

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

if ((victim = last (bin)) != bin)
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;

if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache.  */
size_t tc_idx = csize2tidx (nb);
if (tcache != NULL && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;

/* While bin not empty and tcache not full, copy chunks over.  */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
{
if (tc_victim != 0)
{
bck = tc_victim->bk;
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;

tcache_put (tc_victim, tc_idx);
}
}
}
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}

malloc_consolidate

рдЕрдЧрд░ рдпрд╣ рдПрдХ рдЫреЛрдЯрд╛ рдЪрдВрдХ рдирд╣реАрдВ рдерд╛, рддреЛ рдпрд╣ рдПрдХ рдмрдбрд╝рд╛ рдЪрдВрдХ рд╣реИ, рдФрд░ рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ malloc_consolidate рдХреЛ рдореЗрдореЛрд░реА рдлреНрд░реЗрдЧрдореЗрдВрдЯреЗрд╢рди рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

malloc_consolidate рдХреЙрд▓

```c /* If this is a large request, consolidate fastbins before continuing. While it might look excessive to kill all fastbins before even seeing if there is space available, this avoids fragmentation problems normally associated with fastbins. Also, in practice, programs tend to have runs of either small or large requests, but less often mixtures, so consolidation is not invoked all that often in most programs. And the programs that it is called frequently in otherwise tend to fragment. */

else { idx = largebin_index (nb); if (atomic_load_relaxed (&av->have_fastchunks)) malloc_consolidate (av); }

</details>

malloc рд╕рдореЗрдХрди рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдмреБрдирд┐рдпрд╛рджреА рд░реВрдк рд╕реЗ рддреЗрдЬ рдмрд┐рди рд╕реЗ рдЯреБрдХрдбрд╝реЗ рд╣рдЯрд╛рддреА рд╣реИ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдЕрд╕рдВрдЧрдард┐рдд рдмрд┐рди рдореЗрдВ рд░рдЦрддреА рд╣реИред рдЕрдЧрд▓реЗ malloc рдХреЗ рдмрд╛рдж рдпреЗ рдЯреБрдХрдбрд╝реЗ рдЕрдкрдиреЗ рд╕рдВрдмрдВрдзрд┐рдд рдЫреЛрдЯреЗ/рддреЗрдЬ рдмрд┐рдиреЛрдВ рдореЗрдВ рд╕рдВрдЧрдард┐рдд рд╣реЛ рдЬрд╛рдПрдВрдЧреЗред

рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рдЬрдм рдЗрди рдЯреБрдХрдбрд╝реЛрдВ рдХреЛ рд╣рдЯрд╛рддреЗ рд╕рдордп, рдЕрдЧрд░ рдЙрдиреНрд╣реЗрдВ рдкрд┐рдЫрд▓реЗ рдпрд╛ рдЕрдЧрд▓реЗ рдЯреБрдХрдбрд╝реЛрдВ рдХреЗ рд╕рд╛рде рдкрд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬреЛ рдЙрдкрдпреЛрдЧ рдореЗрдВ рдирд╣реАрдВ рд╣реИрдВ, рддреЛ рд╡реЗ рдЕрдВрдд рдореЗрдВ рдЯреБрдХрдбрд╝реЗ рдХреЛ рдЕрд╕рдВрдЧрдард┐рдд рдмрд┐рди рдореЗрдВ рд░рдЦрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ **рдЕрд▓рд╛рдЗрдХ рдФрд░ рдорд░реНрдЬ** рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред

рдкреНрд░рддреНрдпреЗрдХ рддреЗрдЬ рдмрд┐рди рдЯреБрдХрдбрд╝реЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рд╕реБрд░рдХреНрд╖рд╛ рдЬрд╛рдВрдЪреЗрдВ рдХреА рдЬрд╛рддреА рд╣реИрдВ:

* рдЕрдЧрд░ рдЯреБрдХрдбрд╝рд╛ рдЕрдЕрд▓рд╛рдЗрдиреНрдб рд╣реИ рддреЛ рдЯреНрд░рд┐рдЧрд░ рдХрд░реЗрдВ: `malloc_consolidate(): unaligned fastbin chunk detected`
* рдЕрдЧрд░ рдЯреБрдХрдбрд╝рд╛ рдЙрд╕ рдЖрдХрд╛рд░ рдХрд╛ рдирд╣реАрдВ рд╣реИ рдЬреЛ рдЗрдВрдбреЗрдХреНрд╕ рдХреЗ рдХрд╛рд░рдг рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП: `malloc_consolidate(): invalid chunk size`
* рдЕрдЧрд░ рдкрд┐рдЫрд▓рд╛ рдЯреБрдХрдбрд╝рд╛ рдЙрдкрдпреЛрдЧ рдореЗрдВ рдирд╣реАрдВ рд╣реИ рдФрд░ рдкрд┐рдЫрд▓реЗ рдЯреБрдХрдбрд╝реЗ рдХрд╛ рдЖрдХрд╛рд░ `prev_chunk` рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдЖрдХрд╛рд░ рд╕реЗ рднрд┐рдиреНрди рд╣реИ: `corrupted size vs. prev_size in fastbins`

<details>

<summary>malloc_consolidate рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛</summary>
```c
// https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L4810C1-L4905C2

static void malloc_consolidate(mstate av)
{
mfastbinptr*    fb;                 /* current fastbin being consolidated */
mfastbinptr*    maxfb;              /* last fastbin (for loop control) */
mchunkptr       p;                  /* current chunk being consolidated */
mchunkptr       nextp;              /* next chunk to consolidate */
mchunkptr       unsorted_bin;       /* bin header */
mchunkptr       first_unsorted;     /* chunk to link to */

/* These have same use as in free() */
mchunkptr       nextchunk;
INTERNAL_SIZE_T size;
INTERNAL_SIZE_T nextsize;
INTERNAL_SIZE_T prevsize;
int             nextinuse;

atomic_store_relaxed (&av->have_fastchunks, false);

unsorted_bin = unsorted_chunks(av);

/*
Remove each chunk from fast bin and consolidate it, placing it
then in unsorted bin. Among other reasons for doing this,
placing in unsorted bin avoids needing to calculate actual bins
until malloc is sure that chunks aren't immediately going to be
reused anyway.
*/

maxfb = &fastbin (av, NFASTBINS - 1);
fb = &fastbin (av, 0);
do {
p = atomic_exchange_acquire (fb, NULL);
if (p != 0) {
do {
{
if (__glibc_unlikely (misaligned_chunk (p)))
malloc_printerr ("malloc_consolidate(): "
"unaligned fastbin chunk detected");

unsigned int idx = fastbin_index (chunksize (p));
if ((&fastbin (av, idx)) != fb)
malloc_printerr ("malloc_consolidate(): invalid chunk size");
}

check_inuse_chunk(av, p);
nextp = REVEAL_PTR (p->fd);

/* Slightly streamlined version of consolidation code in free() */
size = chunksize (p);
nextchunk = chunk_at_offset(p, size);
nextsize = chunksize(nextchunk);

if (!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size in fastbins");
unlink_chunk (av, p);
}

if (nextchunk != av->top) {
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

if (!nextinuse) {
size += nextsize;
unlink_chunk (av, nextchunk);
} else
clear_inuse_bit_at_offset(nextchunk, 0);

first_unsorted = unsorted_bin->fd;
unsorted_bin->fd = p;
first_unsorted->bk = p;

if (!in_smallbin_range (size)) {
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}

set_head(p, size | PREV_INUSE);
p->bk = unsorted_bin;
p->fd = first_unsorted;
set_foot(p, size);
}

else {
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
}

} while ( (p = nextp) != 0);

}
} while (fb++ != maxfb);
}

рдЕрдирдХреНрд░рдорд┐рдд рдмрд┐рди

рдпрд╣ рд╕рдордп рд╣реИ рдЕрдирдХреНрд░рдорд┐рдд рдмрд┐рди рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдВрднрд╛рд╡рд┐рдд рд╡реИрдз рдЪрдВрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдПред

рдкреНрд░рд╛рд░рдВрдн

рдпрд╣ рдПрдХ рдмрдбрд╝реЗ рдлреЙрд░ рд▓реБрдХ рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ рдЬреЛ рдЕрдирдХреНрд░рдорд┐рдд рдмрд┐рди рдХреЛ 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)

_int_malloc рдЕрдирдХреНрд░рдорд┐рдд рдмрд┐рди рдкреНрд░рд╛рд░рдВрдн

```c /* Process recently freed or remaindered chunks, taking one only if it is exact fit, or, if this a small request, the chunk is remainder from the most recent non-exact fit. Place other traversed chunks in bins. Note that this step is the only place in any routine where chunks are placed in bins.

The outer loop here is needed because we might not realize until near the end of malloc that we should have consolidated, so must do so and retry. This happens at most once, and only when we would otherwise need to expand memory to service a "small" request. */

#if USE_TCACHE INTERNAL_SIZE_T tcache_nb = 0; size_t tc_idx = csize2tidx (nb); if (tcache != NULL && tc_idx < mp_.tcache_bins) tcache_nb = nb; int return_cached = 0;

tcache_unsorted_count = 0; #endif

for (;; ) { int iters = 0; while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)) { bck = victim->bk; size = chunksize (victim); mchunkptr next = chunk_at_offset (victim, size);

if (__glibc_unlikely (size <= CHUNK_HDR_SZ) || __glibc_unlikely (size > av->system_mem)) malloc_printerr ("malloc(): invalid size (unsorted)"); if (__glibc_unlikely (chunksize_nomask (next) < CHUNK_HDR_SZ) || __glibc_unlikely (chunksize_nomask (next) > av->system_mem)) malloc_printerr ("malloc(): invalid next size (unsorted)"); if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size)) malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)"); if (__glibc_unlikely (bck->fd != victim) || __glibc_unlikely (victim->fd != unsorted_chunks (av))) malloc_printerr ("malloc(): unsorted double linked list corrupted"); if (__glibc_unlikely (prev_inuse (next))) malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)");

</details>

#### рдпрджрд┐ `in_smallbin_range`

рдпрджрд┐ рдЪрдВрдХ рдЕрдиреБрд░реЛрдзрд┐рдд рдЖрдХрд╛рд░ рд╕реЗ рдмрдбрд╝рд╛ рд╣реИ рддреЛ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ, рдФрд░ рдЪрдВрдХ рд╕реНрдерд╛рди рдХрд╛ рд╢реЗрд╖ рдЕрдВрд╢ рдЕрд╡реНрдпрд╡рд╕реНрдерд┐рдд рд╕реВрдЪреА рдореЗрдВ рдбрд╛рд▓реЗрдВ рдФрд░ `last_remainder` рдХреЛ рдЗрд╕рдХреЗ рд╕рд╛рде рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВред

<details>

<summary><code>_int_malloc</code> рдЕрд╡реНрдпрд╡рд╕реНрдерд┐рдд рдмрд┐рди <code>in_smallbin_range</code></summary>
```c
// From https://github.com/bminor/glibc/blob/master/malloc/malloc.c#L4090C11-L4124C14

/*
If a small request, try to use last remainder if it is the
only chunk in unsorted bin.  This helps promote locality for
runs of consecutive small requests. This is the only
exception to best-fit, and applies only when there is
no exact fit for a small chunk.
*/

if (in_smallbin_range (nb) &&
bck == unsorted_chunks (av) &&
victim == av->last_remainder &&
(unsigned long) (size) > (unsigned long) (nb + MINSIZE))
{
/* split and reattach remainder */
remainder_size = size - nb;
remainder = chunk_at_offset (victim, nb);
unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
av->last_remainder = remainder;
remainder->bk = remainder->fd = unsorted_chunks (av);
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;
}

рдпрджрд┐ рдпрд╣ рд╕рдлрд▓ рд░рд╣рд╛, рддреЛ рдЯреБрдХрдбрд╝рд╛ рдФрд░ рдЦрддреНрдо рд╣реЛ рдЧрдпрд╛, рдЕрдЧрд░ рдирд╣реАрдВ, рддреЛ рдХрд╛рд░реНрдп рдХреЛ рдЬрд╛рд░реА рд░рдЦреЗрдВ...

рдпрджрд┐ рдмрд░рд╛рдмрд░ рдЖрдХрд╛рд░ рд╣реИ

рдЪрдВрдХ рдХреЛ рдмрд┐рди рд╕реЗ рд╣рдЯрд╛рдирд╛ рдЬрд╛рд░реА рд░рдЦреЗрдВ, рдорд╛рдорд▓реЗ рдореЗрдВ рдЕрдЧрд░ рдЪрдВрдХ рдХрд╛ рдЖрд╡рд╢реНрдпрдХ рдЖрдХрд╛рд░ рдмрд┐рд▓реНрдХреБрд▓ рдЙрд╕реА рд╣реИ:

  • рдпрджрд┐ рдЯреАрдХреИрд╢ рднрд░рд╛ рдирд╣реАрдВ рд╣реИ, рддреЛ рдЗрд╕реЗ рдЯреАрдХреИрд╢ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдЬрд╛рд░реА рд░рдЦреЗрдВ рдХрд┐ рдПрдХ рдЯреАрдХреИрд╢ рдЪрдВрдХ рд╣реИ рдЬреЛ рдкреНрд░рдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ

  • рдпрджрд┐ рдЯреАрдХреИрд╢ рднрд░рд╛ рд╣реИ, рддреЛ рдЗрд╕реЗ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рдФрд░ рдЗрд╕реЗ рд╡рд╛рдкрд╕ рдХрд░реЗрдВред

_int_malloc рдЕрдирд╕реЙрд░реНрдЯреЗрдб рдмрд┐рди рдмрд░рд╛рдмрд░ рдЖрдХрд╛рд░

```c // 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 }

</details>

рдпрджрд┐ рдЪрдВрдХ рд╡рд╛рдкрд╕ рдирд╣реАрдВ рд▓реМрдЯрд╛рдпрд╛ рдЧрдпрд╛ рдпрд╛ рдЯреАрдХреИрдЪ рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдирд╣реАрдВ рдЧрдпрд╛ рд╣реИ, рдХреЛрдб рдХреЗ рд╕рд╛рде рдЬрд╛рд░реА рд░рдЦреЗрдВ...

#### рдмрд┐рди рдореЗрдВ рдЪрдВрдХ рд░рдЦреЗрдВ

рдЪреЗрдХ рдХрд┐рдП рдЧрдП рдЪрдВрдХ рдХреЛ рдЫреЛрдЯреЗ рдмрд┐рди рдореЗрдВ рдпрд╛ рдмрдбрд╝реЗ рдмрд┐рди рдореЗрдВ рд░рдЦреЗрдВ рдЪрдВрдХ рдХреЗ рдЖрдХрд╛рд░ рдХреЗ рдЕрдиреБрд╕рд╛рд░ (рдмрдбрд╝реЗ рдмрд┐рди рдХреЛ рдареАрдХ рд╕реЗ рд╕рдВрдЧрдард┐рдд рд░рдЦрддреЗ рд╣реБрдП)ред

рдпрд╣рд╛рдБ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реБрд░рдХреНрд╖рд╛ рдЬрд╛рдВрдЪреЗрдВ рдХреА рдЬрд╛ рд░рд╣реА рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдмрдбрд╝реЗ рдмрд┐рди рдбрдмрд▓ рд▓рд┐рдВрдХреНрдб рд╕реВрдЪреА рдХреЛ рдХреЛрд░рдкреНрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ:

* рдпрджрд┐ `fwd->bk_nextsize->fd_nextsize != fwd`: `malloc(): largebin double linked list corrupted (nextsize)`
* рдпрджрд┐ `fwd->bk->fd != fwd`: `malloc(): largebin double linked list corrupted (bk)`

<details>

<summary><code>_int_malloc</code> рдмрд┐рди рдореЗрдВ рдЪрдВрдХ рд░рдЦреЗрдВ</summary>
```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 рд╕реЗ рдПрдХ рдЪрдВрдХ рд╡рд╛рдкрд╕ рд▓реМрдЯрд╛рдПрдВред

_int_malloc рд╕реАрдорд╛рдПрдБ

```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

</details>

рдпрджрд┐ рд╕реАрдорд╛рдПрдБ рдирд╣реАрдВ рдкрд╣реБрдВрдЪреА рд╣реИрдВ, рддреЛ рдХреЛрдб рдХреЗ рд╕рд╛рде рдЬрд╛рд░реА рд░рдЦреЗрдВ...

### рдмрдбрд╝рд╛ рдмрд┐рди (рдЗрдВрдбреЗрдХреНрд╕ рджреНрд╡рд╛рд░рд╛)

рдпрджрд┐ рдЕрдиреБрд░реЛрдз рдмрдбрд╝рд╛ рд╣реИ (рдЫреЛрдЯреЗ рдмрд┐рди рдореЗрдВ рдирд╣реАрдВ рд╣реИ) рдФрд░ рд╣рдордиреЗ рдЕрдм рддрдХ рдХреЛрдИ рднреА рдЯреБрдХрдбрд╝рд╛ рд╡рд╛рдкрд╕ рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИ, рддреЛ **рдмрдбрд╝реЗ рдмрд┐рди** рдореЗрдВ рдЕрдиреБрд░реЛрдзрд┐рдд рдЖрдХрд╛рд░ рдХрд╛ **рдЗрдВрдбреЗрдХреНрд╕** рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЬрд╛рдВрдЪреЗрдВ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ **рдЦрд╛рд▓реА рдирд╣реАрдВ рд╣реИ** рдпрд╛ рдХреНрдпрд╛ **рдЗрд╕ рдмрд┐рди рдореЗрдВ рд╕рдмрд╕реЗ рдмрдбрд╝рд╛ рдЯреБрдХрдбрд╝рд╛ рдЕрдиреБрд░реЛрдзрд┐рдд рдЖрдХрд╛рд░ рд╕реЗ рдмрдбрд╝рд╛ рд╣реИ** рдФрд░ рдЙрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ **рдЙрд╕ рдЫреЛрдЯреЗ рдЯреБрдХрдбрд╝реЗ рдХреЛ рдЦреЛрдЬреЗрдВ рдЬреЛ рдЕрдиреБрд░реЛрдзрд┐рдд рдЖрдХрд╛рд░ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ**ред

рдЕрдВрдд рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЧрдП рдЯреБрдХрдбрд╝реЗ рд╕реЗ рд╢реЗрд╖ рд╕реНрдерд╛рди рдПрдХ рдирдП рдЯреБрдХрдбрд╝реЗ рдХреЗ рд▓рд┐рдП рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рддреЛ рдЗрд╕реЗ рдЕрд╡реНрдпрд╡рд╕реНрдерд┐рдд рдмрд┐рди рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ рдФрд░ lsast\_reminder рдЕрдкрдбреЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдЬрдм рдЕрд╡реНрдпрд╡рд╕реНрдерд┐рдд рдмрд┐рди рдореЗрдВ рдпрд╛рджрджрд╛рд╢реНрдд рдЬреЛрдбрд╝реА рдЬрд╛рддреА рд╣реИ, рддреЛ рдПрдХ рд╕реБрд░рдХреНрд╖рд╛ рдЬрд╛рдВрдЪ рдХреА рдЬрд╛рддреА рд╣реИ:

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

<details>

<summary><code>_int_malloc</code> рдмрдбрд╝рд╛ рдмрд┐рди (рдЗрдВрдбреЗрдХреНрд╕ рджреНрд╡рд╛рд░рд╛)</summary>
```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