
libtalloc is a python script for use with GDB that can be used to analyse the "trivial allocator" (talloc)
libtalloc is a python script for use with GDB that can be used to analyse the "trivial allocator" (talloc). An introduction is about talloc can be found here:
https://talloc.samba.org/talloc/doc/html/index.html
libtalloc was inspired by other gdb python scripts for analyzing heaps like unmask_jemalloc and libheap. Some basic functionality is identical to these projects.
https://github.com/cloudburst/libheap
https://github.com/argp/unmask_jemalloc
Please note that I am no python guru and the code quality reflects this. If you see something that disgusts you, feel free to send a patch or give me some suggestions. All feedback is welcome.
libtalloc has been tested on a variety of 2.x releases of talloc and supports dynamic version detection in order to try to overcome various structural differences across the versions. It has been tested on 32-bit and 64-bit, however not exhaustively, so don't be surprised if it breaks from time to time.
It has been tested to some degree on x86 and x64:
If you test it on another version, please let me know if it worked, or what broke and I will try to update it and/or the docs accordingly.
The script just requires a relatively modern version of GDB with python support.
Some LTS distros, like Ubuntu 12.04, still use GDB with python 2.7, whereas newer versions like 14.04 use python 3.0. I tried to make this script work with both, so you should only need to:
(gdb) source libtalloc.py
Most of the functionality is modeled after the approach by unmask_jemalloc, where a separate GDB command is provided rather than a complex set of switches.
A number of methods specifically designed to mimic the talloc library C functions are available, to help people trying to extend libtalloc if they're already familiar with the library.
To see a full list of commands you can issue the tchelp command:
(gdb) tchelp
[libtalloc] talloc commands for gdb
[libtalloc] tcchunk -v -x <addr> : show chunk contents (-v for verbose, -x for data dump)
[libtalloc] tcvalidate -a <addr> : validate chunk (-a for whole heap)
[libtalloc] tcsearch <addr> : search heap for hex value or address
[libtalloc] tcwalk <func> : walk whole heap calling func on every chunk
[libtalloc] tcreport <addr> : give talloc_report_full() info on memory context
[libtalloc] tcdump -s <addr> : dump chunks linked to memory context (-s for sorted by addr)
[libtalloc] tcparents <addr> : show all parents of chunk
[libtalloc] tcchildren <addr> : show all children of chunk
[libtalloc] tcinfo : show information known about heap
[libtalloc] tcprobe : try to collect information about talloc version
[libtalloc] tchelp : this help message
One of the most important commands is tcprobe. It needs to be run in order to figure out what version of talloc is actually installed. The structure layouts for different versions can vary significantly, so in order for most functions to work the version must be known.
If the command works, it should tell you the detected version:
(gdb) tcprobe
Version: 2.1.1
File: /usr/lib/libtalloc.so.2.1.1
The tcinfo command is meant to show as much information collected about the heap as possible, such as the information from tcprobe, the null_context structure if it was found, and more. Atm it only shows the version and if the null_context is set. The null_context is required for most of the functions that walk the actual heirarchy, and in order to find it most other functionality, like tchunk, etc will try to auto-find it.
After tcprobe is run but before tchunk is actually used:
(gdb) tcinfo [libtalloc] null_context not yet found yet [libtalloc] Version: 2.0.7 [libtalloc] File: /usr/lib/i386-linux-gnu/libtalloc.so.2.0.7
Then after analyzing a chunk, like so:
(gdb) tcchunk 0xb94a52b0 WARNING: 0xb94a52b0 not a talloc_chunk. Assuming ptr to chunk data 0xb94a5280 sz:0x0000003c, flags:...., name:struct tevent_context
You can confirm that it was found after the fact using tcinfo.
(gdb) tcinfo [libtalloc] null_context: 0xb94a5028 [libtalloc] Version: 2.0.7 [libtalloc] File: /usr/lib/i386-linux-gnu/libtalloc.so.2.0.7
Now that the null_context is set you could run other commands that would normally complain that it wasn't set, like the tcsearch command.
tcchunk can provide you with a summary of the chunk, a more verbose output of every field, or extremely verbose information about every surrounding chunk.
NOTE: One important think to note about tcchunk is that internally it uses the tc_chunk() method, which attempts to correct errors made when passing in the chunk address. Specifically if you pass in the address of the chunk data itself, if it doesn't find the expected talloc magic, it will look for a legitimate chunk header slightly earlier in memory. This can mess with you in corrupted scenarios, so always be sure you're passing in the explicit address unless you're doing cursory analysis.
Summary output:
(gdb) tcchunk 0x80a13c88
0x80a13c88 sz:0x00000020, flags:..p., name:struct netr_ServerPasswordSet
The following is a legend for chunks within the summary output:
p - Member of a pool (POOLMEM flag)
P - Chunk is a pool (POOL flag)
F - Chunk is free (FREE flag)
L - Chunk is looped (LOOP flag)
Verbose output:
(gdb) tcchunk -v 0x80a13c88
struct talloc_chunk @ 0x80a13c88 {
next = 0x0
prev = 0x80a140c8
parent = 0x0
child = 0x80a14088
refs = 0x0
destructor = 0x0
name = 0x807d9f2f (struct netr_ServerPasswordSet)
size = 0x20
flags = 0xe8150c78 (POOLMEM)
limit = 0x0
pool = 0x80a13248
talloc chunks contain some magic values that can be used to validate if they are sane. The tcvalidate command will analyze a chunk to ensure that the chunk magic is as expected. Additionally, it analyzes all other pointer members to ensure they actually fall into memory ranges (as known by gdb), if the size is valid, etc.
(gdb) tcvalidate 0x80a13c88
Chunk header is valid
We'll use a built-in method to modify a value to show how it could fail:
(gdb) python set_destructor(tc_chunk(0x80a13c88), 0x41414141)
(gdb) tcchunk -v 0x80a13c88
struct talloc_chunk @ 0x80a13c88 {
next = 0x0
prev = 0x80a140c8
parent = 0x0
child = 0x80a14088
refs = 0x0
destructor = 0x41414141
name = 0x807d9f2f (struct netr_ServerPasswordSet)
size = 0x20
flags = 0xe8150c78 (POOLMEM)
limit = 0x0
pool = 0x80a13248
(gdb) tcvalidate 0x80a13c88
Chunk header is invalid:
0x80a13c88: Chunk has bad destructor pointer 0x41414141
tcparents can be used to view all parents of the provided chunk:
(gdb) tcparents 0x80a13c88
0x809f8300: null_context
0x80a08660: TALLOC_CTX *
0x809f8370: talloc_new: ../lib/util/talloc_stack.c:147
0x809fb680: talloc_new: ../lib/util/talloc_stack.c:147
0x80a13258: UNNAMED
0x80a13c58: talloc_new: ../lib/util/talloc_stack.c:147
0x80a13c88: struct netr_ServerPasswordSet
tchildren can be used to view all children (and grandchildren, etc) of the provided chunk: