// Test the find_fake_fast command. #include #include #include #include #include // Part of this test requires unmapped virtual memory at a specific alignment. #define LIKELY_UNMAPPED_MEMORY_ADDRESS (void *)0x500000000000 /* GLIBC's HEAP_MAX_SIZE constant, used to align the aforementioned unmapped virtual memory. * HEAP_MAX_SIZE is defined at: * https://github.com/bminor/glibc/blob/f704192911c6c7b65a54beab3ab369fca7609a5d/malloc/arena.c#L31 * It is calculated using DEFAULT_MMAP_THRESHOLD_MAX, which is defined at: * https://github.com/bminor/glibc/blob/f704192911c6c7b65a54beab3ab369fca7609a5d/malloc/malloc.c#L956 * (x64 architecture is assumed for this test) */ #define HEAP_MAX_SIZE (2 * 4 * 1024 * 1024 * sizeof(long)) void break_here(void) {} // Fake chunk size field for use with issue #1142 test. char *fake_chunk = NULL; // This buffer will contain the fake chunk sizes unsigned long buf[64] __attribute__((aligned(0x10))); // This is the address we want the fake chunks to overlap with unsigned long target_address; /* * Put the value of `size` at `distance` bytes before the address of * `target_address` */ void setup_mem(unsigned long size, unsigned distance) { memset(buf, 0, sizeof(buf)); target_address = 0; char *chunk_size_addr = (char *)&target_address - distance; *(unsigned long *)chunk_size_addr = size; } int main(void) { assert((unsigned long)&target_address - (unsigned long)buf == sizeof(buf)); /* Test whether the find_fake_fast command can deal with a fake chunk that has a set * NON_MAIN_ARENA flag, but no heap_info struct (the struct would reside in unmapped memory). * Issue #1142 */ void *aligned_memory = NULL; for (void *requested_address = LIKELY_UNMAPPED_MEMORY_ADDRESS; requested_address > 0; requested_address -= HEAP_MAX_SIZE) { // Attempt to find unmapped memory aligned to HEAP_MAX_SIZE. void *mmapped_address = mmap(requested_address, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mmapped_address == requested_address) { aligned_memory = mmapped_address; break; } else { munmap(mmapped_address, 2 * getpagesize()); } } assert(aligned_memory != NULL); // Set up a candidate fake fast chunk size field with a set NON_MAIN_ARENA flag. fake_chunk = aligned_memory + getpagesize(); fake_chunk[8] = '\x85'; // Unmap the memory where the fake chunk's heap_info struct would reside. int unmapped = munmap(aligned_memory, getpagesize()); assert(unmapped == 0); // Initialize malloc so heap commands can run. void *m = malloc(0x18); // A valid aligned fastbin chunk with no flags set setup_mem(0x20, 0x8); break_here(); // A valid aligned fastbin chunk with all flags set setup_mem(0x2F, 0x8); break_here(); // A valid unaligned fastbin chunk setup_mem(0x20, 0x9); break_here(); // A valid aligned fastbin chunk that's too close to the target address (the // size overlaps the target address) setup_mem(0x20, 0x0); break_here(); // A valid unaligned fastbin chunk that's too close to the target address (the // size overlaps the target address) setup_mem(0x20, 0x7); break_here(); // An invalid chunk with a size below the minimum chunk size setup_mem(0x1F, 0x8); break_here(); // A valid aligned fastbin chunk just in range of the target address setup_mem(0x80, 0x78); break_here(); /* // A valid unaligned fastbin chunk just in range of the target address */ /* setup_mem(0x80, 0x7F); */ /* break_here(); */ // A valid aligned fastbin chunk just out of range of the target address setup_mem(0x80, 0x80); break_here(); // A fastbin chunk with a size greater than `global_max_fast`, less than // `global_max_fast` bytes away from the target address setup_mem(0x100, 0x10); break_here(); // A fastbin chunk with a size greater than `global_max_fast`, more than // `global_max_fast` bytes away from the target address setup_mem(0x100, 0x90); break_here(); // A fastbin chunk with a size greater than `global_max_fast`, just out of // range of the target address setup_mem(0x100, 0x100); break_here(); // A fastbin chunk with a size that has top 4 bytes clobbered, due to glibc // fastbin size bug, this is still valid setup_mem(0xAABBCCDD00000020, 0x8); break_here(); // A fastbin chunk that overlaps only the first byte of the target address // Used to test the --partial-overwrite option setup_mem(0x8000, 0x80); break_here(); }