From d8fbdc1b888d820f9f9d41d568b10008804fea08 Mon Sep 17 00:00:00 2001 From: Alan Li <61896187+lebr0nli@users.noreply.github.com> Date: Mon, 31 Oct 2022 04:45:11 +0800 Subject: [PATCH] Support `malloc_par` of GLIBC 2.35 (#1353) --- pwndbg/heap/structs.py | 80 ++++++++++++++++++++++++- tests/gdb-tests/tests/heap/test_heap.py | 22 +++---- 2 files changed, 86 insertions(+), 16 deletions(-) diff --git a/pwndbg/heap/structs.py b/pwndbg/heap/structs.py index a347b938e..7ab8b9be1 100644 --- a/pwndbg/heap/structs.py +++ b/pwndbg/heap/structs.py @@ -629,12 +629,90 @@ class c_malloc_par_2_26(ctypes.LittleEndianStructure): ] +class c_malloc_par_2_35(ctypes.LittleEndianStructure): + """ + This class represents the malloc_par struct for GLIBC >= 2.35 as a ctypes struct. + + https://github.com/bminor/glibc/blob/glibc-2.35/malloc/malloc.c#L1874 + + struct malloc_par + { + /* Tunable parameters */ + unsigned long trim_threshold; + INTERNAL_SIZE_T top_pad; + INTERNAL_SIZE_T mmap_threshold; + INTERNAL_SIZE_T arena_test; + INTERNAL_SIZE_T arena_max; + + #if HAVE_TUNABLES + /* Transparent Large Page support. */ + INTERNAL_SIZE_T thp_pagesize; + /* A value different than 0 means to align mmap allocation to hp_pagesize + add hp_flags on flags. */ + INTERNAL_SIZE_T hp_pagesize; + int hp_flags; + #endif + + /* Memory map support */ + int n_mmaps; + int n_mmaps_max; + int max_n_mmaps; + /* the mmap_threshold is dynamic, until the user sets + it manually, at which point we need to disable any + dynamic behavior. */ + int no_dyn_threshold; + + /* Statistics */ + INTERNAL_SIZE_T mmapped_mem; + INTERNAL_SIZE_T max_mmapped_mem; + + /* First address handed out by MORECORE/sbrk. */ + char *sbrk_base; + + #if USE_TCACHE + /* Maximum number of buckets to use. */ + size_t tcache_bins; + size_t tcache_max_bytes; + /* Maximum number of chunks in each bucket. */ + size_t tcache_count; + /* Maximum number of chunks to remove from the unsorted list, which + aren't used to prefill the cache. */ + size_t tcache_unsorted_limit; + #endif + }; + """ + + _fields_ = [ + ("trim_threshold", c_size_t), + ("top_pad", c_size_t), + ("mmap_threshold", c_size_t), + ("arena_test", c_size_t), + ("arena_max", c_size_t), + ("thp_pagesize", c_size_t), + ("hp_pagesize", c_size_t), + ("hp_flags", ctypes.c_int32), + ("n_mmaps", ctypes.c_int32), + ("n_mmaps_max", ctypes.c_int32), + ("max_n_mmaps", ctypes.c_int32), + ("no_dyn_threshold", ctypes.c_int32), + ("mmapped_mem", c_size_t), + ("max_mmapped_mem", c_size_t), + ("sbrk_base", c_pvoid), + ("tcache_bins", c_size_t), + ("tcache_max_bytes", c_size_t), + ("tcache_count", ctypes.c_int32), + ("tcache_unsorted_limit", c_size_t), + ] + + class MallocPar(CStruct2GDB): """ This class represents the malloc_par struct with interface compatible with `gdb.Value`. """ - if pwndbg.glibc.get_version() >= (2, 26): + if pwndbg.glibc.get_version() >= (2, 35): + _c_struct = c_malloc_par_2_35 + elif pwndbg.glibc.get_version() >= (2, 26): _c_struct = c_malloc_par_2_26 else: _c_struct = c_malloc_par_2_25 diff --git a/tests/gdb-tests/tests/heap/test_heap.py b/tests/gdb-tests/tests/heap/test_heap.py index 4c5f8f9c4..43a26def2 100644 --- a/tests/gdb-tests/tests/heap/test_heap.py +++ b/tests/gdb-tests/tests/heap/test_heap.py @@ -281,14 +281,10 @@ def test_mp_heuristic(start_binary): # Check the address of `main_arena` is correct assert pwndbg.heap.current.mp.address == mp_addr_via_debug_symbol # Check the struct size is correct - # FIXME: We still have bug for GLIBC >= 2.35 in this heuristic because the size of `malloc_par` is changed - # So this test will fail for the tests on ubuntu 22.04 - # TODO: Fix the bug and enable this test - if pwndbg.glibc.get_version() < (2, 35): - assert ( - pwndbg.heap.current.mp.type.sizeof - == pwndbg.gdblib.typeinfo.lookup_types("struct malloc_par").sizeof - ) + assert ( + pwndbg.heap.current.mp.type.sizeof + == pwndbg.gdblib.typeinfo.lookup_types("struct malloc_par").sizeof + ) pwndbg.heap.current = type(pwndbg.heap.current)() # Reset the heap object of pwndbg # Level 2: We check we can get the address of `mp_` by parsing the assembly code of `__libc_free` @@ -299,13 +295,9 @@ def test_mp_heuristic(start_binary): pwndbg.heap.current = type(pwndbg.heap.current)() # Reset the heap object of pwndbg # Level 3: We check we can get the address of `mp_` by parsing the memory - # FIXME: We still have bug for GLIBC >= 2.35 in this heuristic because the size of `malloc_par` is changed - # So this test will fail for the tests on ubuntu 22.04 - # TODO: Fix the bug and enable this test - if pwndbg.glibc.get_version() < (2, 35): - with mock_for_heuristic(mock_all=True): - # Check the address of `mp_` is correct - assert pwndbg.heap.current.mp.address == mp_addr_via_debug_symbol + with mock_for_heuristic(mock_all=True): + # Check the address of `mp_` is correct + assert pwndbg.heap.current.mp.address == mp_addr_via_debug_symbol def test_global_max_fast_heuristic(start_binary):