Analyzing glibc2.24 malloc internals on a 64-bit machine, I'm using both parseheap & heapinfo functionalities.
Basically, when we use heapinfo we get "wrong" index number for largebins.
It will be easier to explain with an example.
1 heap_k.c X
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NSMALLBINS 64 // from glibc 2.24
// actual calculation of NSMALLBINS
#define NUNSORTEDBINS 1
#define MIN_LARGE_SIZE (NSMALLBINS * 0x10)
#define MIN_CHUNK_SIZE 0x20
#define CHUNKS_SKIP ((MIN_CHUNK_SIZE / 0x10) - 1)
#define OVER_HEAD 0x8
#define REAL_NSAMLLBINS ((MIN_LARGE_SIZE - (NUNSORTEDBINS * 0x10)) / 0x10 - CHUNKS_SKIP) // 62
void main(){
setvbuf(stdin,NULL,_IONBF,0); // no buffering
setvbuf(stdout,NULL,_IONBF,0); // no buffering
char* a00 = (char*) malloc(512); // no coalescing
char* a01 = (char*) malloc(0xb0); // smallbin will be splitted for malloc(0x0) request
char* a02 = (char*) malloc(512); // no coalescing
char* a03 = (char*) malloc(REAL_NSAMLLBINS * 0x10 + CHUNKS_SKIP * 0x10 - OVER_HEAD); // biggest smallbin chunk possible
char* a04 = (char*) malloc(512); // no coalescing
char* a05 = (char*) malloc(REAL_NSAMLLBINS * 0x10 + CHUNKS_SKIP * 0x10 - OVER_HEAD + 1); // smallest largebin chunk possible
char* a06 = (char*) malloc(512); // no coalescing
char* a07 = (char*) malloc(9000); // random largebin chunk
char* a08 = (char*) malloc(512); // no coalescing
free(a01);
free(a03);
free(a05);
free(a07);
char* a09 = (char*) malloc(0x90); // a01 is spillted to size 0xb0-0x90 = 0x20
char* a10 = (char*) malloc(0x9001); // sort unorderedbin
}
~
What this example does is forcing a split of chunks so a chunk of size 0x20 (smallest) will be inserted to the 0x20 smallbin which is the first one. Then it allocates the biggest chunk that fits smallbin requirments 0x3f0 - 8 so it will be inserted to the 0x3f0 which is the last smallbin.
Afterwards it allocates the smallest chunk that fits largebin requirments 0x3f0 + 1 so it will be inserted to the first largebin.
The calculation problem, lets break into this program at the end of main.
gdb heap_h -x utils.sh
# utils.sh
...
define hip
parseheap
heapinfo
end
...
output
gdb-peda$ hip
addr prev size status fd bk
0x555555756000 0x0 0x210 Used None None
0x555555756210 0x0 0xa0 Used None None
0x5555557562b0 0x0 0x20 Freed 0x7ffff7dd3b68 0x7ffff7dd3b68
0x5555557562d0 0x20 0x210 Used None None
0x5555557564e0 0x0 0x3f0 Freed 0x7ffff7dd3f38 0x7ffff7dd3f38
0x5555557568d0 0x3f0 0x210 Used None None
0x555555756ae0 0x0 0x400 Freed 0x7ffff7dd3f48 0x7ffff7dd3f48
0x555555756ee0 0x400 0x210 Used None None
0x5555557570f0 0x0 0x2330 Freed 0x7ffff7dd4208 0x7ffff7dd4208
0x555555759420 0x2330 0x210 Used None None
0x555555759630 0x0 0x9010 Used None None
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
top: 0x555555762640 (size : 0x149c0)
last_remainder: 0x5555557562b0 (size : 0x20)
unsortbin: 0x0
(0x3f0) smallbin[61]: 0x5555557564e0
(0x020) smallbin[ 0]: 0x5555557562b0
largebin[64]: 0x555555756ae0 (size : 0x400)
largebin[108]: 0x5555557570f0 (size : 0x2330)
gdb-peda$
heapinfo output shows us the "a05" is in the 64th largebin, it should be the 1st (0) in largebins.
Sidenote, both smallbins and largebins are part of a bigger array in "malloc_state" struct called "bins".
This array length is determined at compile time:
// glibc2.24
// https://github.com/bminor/glibc/blob/release/2.24/master/malloc/malloc.c
#define NBINS 128
The first bin is the unsortedbin, then (in this example) 62 smallbins and the rest should be largebins (63?)..
Example of calculating the last smallbin and first largebin address in malloc_state.
Check main_arena (malloc_state) struct
gdb-peda$ p main_arena
$1 = {
mutex = 0x0,
flags = 0x1,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x555555762ad0,
last_remainder = 0x555555756740,
bins = {0x7ffff7dd3b58 <main_arena+88>, 0x7ffff7dd3b58 <main_arena+88>, 0x555555756740, 0x555555756740, 0x555555756000,
...
calculate
gdb-peda$ set $unorderedbin=(long long int)(&main_arena.bins)
gdb-peda$ set $last_smallbin=(long long int)(&main_arena.bins) + 0x10 * 62
gdb-peda$ set $first_largebin=(long long int)(&main_arena.bins) + 0x10 * 63
gdb-peda$ x $unorderedbin
0x7ffff7dd3b68 <main_arena+104>: 0x00007ffff7dd3b58
gdb-peda$ x $last_smallbin
0x7ffff7dd3f48 <main_arena+1096>: 0x00005555557564e0
gdb-peda$ x $first_largebin
0x7ffff7dd3f58 <main_arena+1112>: 0x0000555555756ae0