Repository: pwndbg/pwndbg

Issue:
Some 32-bit architectures might find the wrong `tcache` 
I mess up the code here by accident:
https://github.com/pwndbg/pwndbg/blob/be306da2553596443d2bdaa378bf11cf59e3eab5/pwndbg/heap/ptmalloc.py#L1182
Since somehow this hard-coded 0x10 coincidentally does the [alignment](https://github.com/pwndbg/pwndbg/blob/be306da2553596443d2bdaa378bf11cf59e3eab5/pwndbg/heap/ptmalloc.py#L396-L398) for i386, so i386 won't be affected by this issue. 
But this will not work for other 32-bit architectures whose heap starts with `mp_.sbrk_base` and don't need that alignment, for example, MIPS32.

---

This issue can be reproduced by:
```console
$ cat test.c
#include <stdlib.h>

int main(){
    free(malloc(0x20));
    return 0;
}
$ mips-linux-gnu-gcc test.c -ggdb
$ qemu-mips -g 1234 -L /tmp/usr/mips-linux-gnu ./a.out
```

The libc I used is from: http://kr.archive.ubuntu.com/ubuntu/pool/universe/c/cross-toolchain-base-mipsen/libc6-mips-cross_2.35-0ubuntu1cross1_all.deb
```console
$ file /tmp/usr/mips-linux-gnu/lib/libc.so.6
file /tmp/usr/mips-linux-gnu/lib/libc.so.6
/tmp/usr/mips-linux-gnu/lib/libc.so.6: ELF 32-bit MSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, BuildID[sha1]=5e2197f2ab3148552be4c052016f7ad956f28636, for GNU/Linux 3.2.0, with debug_info, not stripped
```

Then:
```console
$ gdb -q ./a.out -ex 'set context-sections' -ex 'set exception-verbose on' -ex 'set solib-absolute-prefix /tmp/usr/mips-linux-gnu/lib' -ex 'set solib-search-path /tmp/usr/mips-linux-gnu/lib' -ex 'target remote :1234' -ex 'break main' -ex 'continue' -ex 'next' -ex 'heap' -ex 'tcache'
...
Allocated chunk | PREV_INUSE
Addr: 0x412000
Size: 0x189

Allocated chunk | PREV_INUSE
Addr: 0x412188
Size: 0x29

Top chunk | PREV_INUSE
Addr: 0x4121b0
Size: 0x20e51

tcache is pointing to: 0x412010 for thread 1
{
  counts = {0 <repeats 64 times>},
  entries = {0x0, 0x412190, 0x0 <repeats 61 times>, 0x29}
}
```

The result should be 0x412000+4*2 (0x412008), not 0x412010.


Discussion:

Resolving patch:
diff --git a/pwndbg/heap/ptmalloc.py b/pwndbg/heap/ptmalloc.py
--- a/pwndbg/heap/ptmalloc.py
+++ b/pwndbg/heap/ptmalloc.py
@@ -1351,7 +1351,6 @@ def thread_cache(self):
         thread's tcache.
         """
         if self.has_tcache():
-            tcache = self.get_sbrk_heap_region().vaddr + 0x10
             if self.multithreaded:
                 tcache_addr = pwndbg.gdblib.memory.pvoid(
                     pwndbg.gdblib.symbol.static_linkage_symbol_address("tcache")
@@ -1361,6 +1360,8 @@ def thread_cache(self):
                     # This thread doesn't have a tcache yet
                     return None
                 tcache = tcache_addr
+            else:
+                tcache = self.main_arena.heaps[0].start + pwndbg.gdblib.arch.ptrsize * 2
 
             try:
                 self._thread_cache = pwndbg.gdblib.memory.poi(self.tcache_perthread_struct, tcache)
@@ -1780,18 +1781,9 @@ def validator(guess: int) -> bool:
             )
 
         # TODO: The result might be wrong if the arena is being shared by multiple thread
-        ptr_size = pwndbg.gdblib.arch.ptrsize
-
-        cursor = arena.active_heap.start
-
-        # i686 alignment heuristic
-        first_chunk_size = pwndbg.gdblib.arch.unpack(
-            pwndbg.gdblib.memory.read(cursor + ptr_size, ptr_size)
+        self._thread_cache = self.tcache_perthread_struct(
+            arena.heaps[0].start + pwndbg.gdblib.arch.ptrsize * 2
         )
-        if first_chunk_size == 0:
-            cursor += ptr_size * 2
-
-        self._thread_cache = self.tcache_perthread_struct(cursor + ptr_size * 2)
         self._thread_caches[gdb.selected_thread().global_num] = self._thread_cache
 
         return self._thread_cache


Test patch: