structmalloc_chunk{INTERNAL_SIZE_Tmchunk_prev_size;/* Size of previous chunk (if free). */INTERNAL_SIZE_Tmchunk_size;/* Size in bytes, including overhead. */structmalloc_chunk*fd;/* double links -- used only if free. */structmalloc_chunk*bk;/* Only used for large blocks: pointer to next larger size. */structmalloc_chunk*fd_nextsize;/* double links -- used only if free. */structmalloc_chunk*bk_nextsize;};
Allocated chunk
code:c
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, in bytes |A|M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_size() bytes) .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| (size of chunk, but used for application data) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Notice how the data of an allocated chunk uses the first attribute (mchunk_prev_size) of the next chunk. mem is the pointer which is returned to the user.
Free chunk
code:c
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`head:' | Size of chunk, in bytes |A|0|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Forward pointer to next chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Back pointer to previous chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Unused space (may be 0 bytes long) .
. .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`foot:' | Size of chunk, in bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
structmalloc_state{/* Serialize access. */__libc_lock_define(,mutex);/* Flags (formerly in max_fast). */intflags;/* Set if the fastbin chunks contain recently inserted free blocks. *//* Note this is a bool but not all targets support atomics on booleans. */inthave_fastchunks;/* Fastbins */mfastbinptrfastbinsY[NFASTBINS];/* Base of the topmost chunk -- not otherwise kept in a bin */mchunkptrtop;/* The remainder from the most recent split of a small request */mchunkptrlast_remainder;/* Normal bins packed as described above */mchunkptrbins[NBINS*2-2];/* Bitmap of bins */unsignedintbinmap[BINMAPSIZE];/* Linked list */structmalloc_state*next;/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */structmalloc_state*next_free;/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */INTERNAL_SIZE_Tattached_threads;/* Memory allocated from the system in this arena. */INTERNAL_SIZE_Tsystem_mem;INTERNAL_SIZE_Tmax_system_mem;};
binには沢山の種類がある
tcache
fastbins
unsorted bins
small bins
large bins
ヒープ領域
1
2
3
4
5
6
7
8
9
10
11
12
13
structmalloc_chunk{size_tprev_size;// Size of previous chunk (if free).
size_tsize;// Size in bytes, including overhead. 下3bitはフラグ
// userに渡されるアドレスはここ
// double links -- used only if free.
void*fd;// next
void*bk;// key
// for large size
void*fd_nextsize;void*bk_nextsize;}
bins
fd
bk
tcache
next
tcache_key
fastbins
next
null
unsorted bins
&main_arena.top
small bins
large bins
tcache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* We overlay this structure on the user-data portion of a chunk when
the chunk is stored in the per-thread cache. */typedefstructtcache_entry{structtcache_entry*next;/* This field exists to detect double frees. */uintptr_tkey;}tcache_entry;/* There is one of these for each thread, which contains the
per-thread cache (hence "tcache_perthread_struct"). Keeping
overall size low is mildly important. Note that COUNTS and ENTRIES
are redundant (we could have just counted the linked list each
time), this is for performance reasons. */typedefstructtcache_perthread_struct{uint16_tcounts[TCACHE_MAX_BINS];tcache_entry*entries[TCACHE_MAX_BINS];}tcache_perthread_struct;
/* Caller must ensure that we know tc_idx is valid and there's room
for more chunks. */static__always_inlinevoidtcache_put(mchunkptrchunk,size_ttc_idx){tcache_entry*e=(tcache_entry*)chunk2mem(chunk);/* Mark this chunk as "in the tcache" so the test in _int_free will
detect a double free. */e->key=tcache_key;e->next=PROTECT_PTR(&e->next,tcache->entries[tc_idx]);tcache->entries[tc_idx]=e;++(tcache->counts[tc_idx]);}
glibc-2.35
PROTECT_PTR
1
2
3
4
5
6
7
8
9
10
11
12
/* Safe-Linking:
Use randomness from ASLR (mmap_base) to protect single-linked lists
of Fast-Bins and TCache. That is, mask the "next" pointers of the
lists' chunks, and also perform allocation alignment checks on them.
This mechanism reduces the risk of pointer hijacking, as was done with
Safe-Unlinking in the double-linked lists of Small-Bins.
It assumes a minimum page size of 4096 bytes (12 bits). Systems with
larger pages provide less entropy, although the pointer mangling
still works. */#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)