From e6574f447f9335510468fbb24dd127d66d8157bd Mon Sep 17 00:00:00 2001 From: Gulshan Singh Date: Sun, 16 Oct 2022 02:31:47 -0700 Subject: [PATCH] Add find_fake_fast test (#1286) * Fix find_fake_fast test name * Add more find_fake_fast tests --- tests/binaries/heap_find_fake_fast.c | 74 ++++++++++++++++- tests/binaries/makefile | 1 + tests/heap/test_find_fake_fast.py | 117 +++++++++++++++++++++++++++ tests/heap/try_find_fake_fast.py | 23 ------ 4 files changed, 190 insertions(+), 25 deletions(-) create mode 100644 tests/heap/test_find_fake_fast.py delete mode 100644 tests/heap/try_find_fake_fast.py diff --git a/tests/binaries/heap_find_fake_fast.c b/tests/binaries/heap_find_fake_fast.c index 3a85b3bef..5d1af4ec8 100644 --- a/tests/binaries/heap_find_fake_fast.c +++ b/tests/binaries/heap_find_fake_fast.c @@ -10,6 +10,7 @@ */ #include +#include void break_here(void) {} @@ -17,10 +18,79 @@ void break_here(void) {} // Enough space afterwards to ensure only this fake size field is a candidate. char fake_chunk[0x80] __attribute__((aligned(0x10))) = "XXXXXXXX\x7f"; -int main(void) -{ +// 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)); // 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(); } diff --git a/tests/binaries/makefile b/tests/binaries/makefile index f56644597..a48d3baab 100644 --- a/tests/binaries/makefile +++ b/tests/binaries/makefile @@ -1,3 +1,4 @@ +ZIGPATH ?= ../../.zig ZIGCC = $(ZIGPATH)/zig cc CC = gcc diff --git a/tests/heap/test_find_fake_fast.py b/tests/heap/test_find_fake_fast.py new file mode 100644 index 000000000..1e5f1c630 --- /dev/null +++ b/tests/heap/test_find_fake_fast.py @@ -0,0 +1,117 @@ +import re + +import gdb + +import pwndbg +import tests + +HEAP_FIND_FAKE_FAST = tests.binaries.get("heap_find_fake_fast.out") + +target_address = None + + +def check_result(result, expected_size): + ptrsize = pwndbg.gdblib.arch.ptrsize + + matches = re.findall(r"\bAddr: (0x[0-9a-f]+)", result) + assert len(matches) == 1 + addr = int(matches[0], 16) + + matches = re.findall(r"\bsize: (0x[0-9a-f]+)", result) + assert len(matches) == 1 + size = int(matches[0], 16) + + assert size == expected_size + + # The chunk can't start too close to the target address + assert addr <= target_address - (2 * ptrsize) + + # Clear the flags + size &= ~0xF + + # The chunk should overlap the target address + assert addr + ptrsize + size > target_address + + +def check_no_results(result): + matches = re.findall(r"\bAddr: (0x[0-9a-f]+)", result) + assert len(matches) == 0 + + +def test_find_fake_fast_command(start_binary): + global target_address + + start_binary(HEAP_FIND_FAKE_FAST) + gdb.execute("break break_here") + gdb.execute("continue") + + # Ensure memory at fake_chunk's heap_info struct isn't mapped. + unmapped_heap_info = pwndbg.heap.ptmalloc.heap_for_ptr( + pwndbg.gdblib.symbol.address("fake_chunk") + ) + assert pwndbg.gdblib.memory.peek(unmapped_heap_info) is None + + # A gdb.MemoryError raised here indicates a regression from PR #1145 + gdb.execute("find_fake_fast (void*)&fake_chunk+0x70") + + target_address = pwndbg.gdblib.symbol.address("target_address") + assert target_address is not None + print(hex(target_address)) + + # setup_mem(0x20, 0x8) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_result(result, 0x20) + gdb.execute("continue") + + # setup_mem(0x2F, 0x8) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_result(result, 0x2F) + gdb.execute("continue") + + # setup_mem(0x20, 0x9) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_result(result, 0x20) + gdb.execute("continue") + + # setup_mem(0x20, 0x0) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_no_results(result) + gdb.execute("continue") + + # setup_mem(0x20, 0x7) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_no_results(result) + gdb.execute("continue") + + # setup_mem(0x1F, 0x8) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_no_results(result) + gdb.execute("continue") + + # setup_mem(0x80, 0x78) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_result(result, 0x80) + gdb.execute("continue") + + # setup_mem(0x80, 0x7F) + # result = gdb.execute("find_fake_fast &target_address", to_string=True) + # check_result(result, 0x80) + # gdb.execute("continue") + + # setup_mem(0x80, 0x80) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_no_results(result) + gdb.execute("continue") + + # setup_mem(0x100, 0x10) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_no_results(result) + gdb.execute("continue") + + # setup_mem(0x100, 0x90) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_no_results(result) + + # setup_mem(0x100, 0x100) + result = gdb.execute("find_fake_fast &target_address", to_string=True) + check_no_results(result) diff --git a/tests/heap/try_find_fake_fast.py b/tests/heap/try_find_fake_fast.py deleted file mode 100644 index 542ed1889..000000000 --- a/tests/heap/try_find_fake_fast.py +++ /dev/null @@ -1,23 +0,0 @@ -import gdb - -import pwndbg -import tests - -HEAP_FIND_FAKE_FAST = tests.binaries.get("heap_find_fake_fast.out") - - -# Ensure find_fake_fast command doesn't error when fake chunk's heap_info -# struct isn't mapped. -def test_find_fake_fast_command(start_binary): - start_binary(HEAP_FIND_FAKE_FAST) - gdb.execute("break break_here") - gdb.execute("continue") - - # Ensure memory at fake_chunk's heap_info struct isn't mapped. - unmapped_heap_info = pwndbg.heap.ptmalloc.heap_for_ptr( - pwndbg.gdblib.symbol.address("fake_chunk") - ) - assert pwndbg.gdblib.memory.peek(unmapped_heap_info) is None - - # A gdb.MemoryError raised here indicates a regression from PR #1145 - gdb.execute("find_fake_fast (void*)&fake_chunk+0x70")