
Most general-purpose allocators manage the heap as a sequence of chunks. Each chunk is a header (metadata the allocator trusts) plus the user area you get back from malloc. Free chunks also carry links for size‑segregated free lists.
Address grows --->
+---------------------------+-------------------------------------+
| chunk header | user area |
| size | flags | (fd,bk)* | |
+---------------------------+-------------------------------------+
^ ^
| └─ pointer returned by malloc() (p)
└─ allocator metadata (not corrupt)
* fd/bk only present/used when the chunk is on a free list.
Adjacent chunks let the allocator coalesce on free:
... [chunk A][chunk B][chunk C] ...
^ free(B) merges with neighbors if A/B/C are free-adjacent
Because metadata sits next to user data, overflows from one user area can hit the next chunk’s header.
Reading fields of a freshly malloc’d object before writing them. Heap memory is uninitialized by malloc and contains stale bytes.
typedef struct { int len; char *buf; } Msg;
Msg *m = malloc(sizeof *m); // not zeroed
if (!m) return;
printf("%d\\\\n", m->len); // X UB: reading garbage
alloc --> init fields --> use //good
alloc --(skip init)--> use //bad
malloc return codesAssuming allocations always succeed and dereferencing NULL on pressure or limits.
char *p = malloc(n);
memcpy(p, src, n); // (x) crash if p == NULL