
libtalloc é um script Python para uso com GDB que pode ser utilizado para analisar o "trivial allocator" (talloc)
O libtalloc é um script python para uso com GDB que pode ser usado para analisar o "alocador trivial" (talloc). Uma introdução sobre o talloc pode ser encontrada aqui:
https://talloc.samba.org/talloc/doc/html/index.html
O libtalloc foi inspirado por outros scripts python para gdb de análise de heaps, como unmask_jemalloc e libheap. Algumas funcionalidades básicas são idênticas às desses projetos.
https://github.com/cloudburst/libheap
https://github.com/argp/unmask_jemalloc
Por favor, note que não sou um guru de python e a qualidade do código reflete isso. Se você vir algo que o desagrada, sinta-se à vontade para enviar um patch ou dar algumas sugestões. Todo feedback é bem-vindo.
O libtalloc foi testado em várias versões 2.x do talloc e suporta detecção dinâmica de versão para tentar contornar as diversas diferenças estruturais entre as versões. Foi testado em 32 bits e 64 bits, porém não de forma exaustiva, então não se surpreenda se quebrar de vez em quando.
Foi testado até certo ponto em x86 e x64:
Se você testá-lo em outra versão, por favor me avise se funcionou ou o que quebrou, e tentarei atualizá-lo e/ou a documentação de acordo.
O script apenas requer uma versão relativamente moderna do GDB com suporte a python.
Algumas distros LTS, como Ubuntu 12.04, ainda usam GDB com python 2.7, enquanto versões mais novas, como 14.04, usam python 3.0. Tentei fazer este script funcionar com ambos, então você só deve precisar de:
(gdb) source libtalloc.py
A maior parte da funcionalidade é modelada a partir da abordagem do unmask_jemalloc, onde um comando GDB separado é fornecido em vez de um conjunto complexo de opções.
Vários métodos projetados especificamente para imitar as funções C da biblioteca talloc estão disponíveis, para ajudar pessoas que tentam estender o libtalloc se já estiverem familiarizadas com a biblioteca.
Para ver uma lista completa de comandos, você pode executar o comando tchelp:
(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
Um dos comandos mais importantes é o tcprobe. Ele precisa ser executado para descobrir qual versão do talloc está realmente instalada. Os layouts de estrutura para diferentes versões podem variar significativamente, portanto, para que a maioria das funções funcione, a versão precisa ser conhecida.
Se o comando funcionar, ele deve informar a versão detectada:
(gdb) tcprobe
Version: 2.1.1
File: /usr/lib/libtalloc.so.2.1.1
O comando tcinfo tem como objetivo mostrar o máximo de informações coletadas sobre o heap possível, como as informações do tcprobe, a estrutura null_context se ela foi encontrada, e mais. No momento, ele mostra apenas a versão e se o null_context está definido. O null_context é necessário para a maioria das funções que percorrem a hierarquia real e, para encontrá-lo, a maioria das outras funcionalidades, como tchunk, etc., tentará localizá-lo automaticamente.
Depois que o tcprobe é executado, mas antes que o tchunk seja realmente usado:
(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
Então, depois de analisar um chunk, assim:
(gdb) tcchunk 0xb94a52b0
WARNING: 0xb94a52b0 not a talloc_chunk. Assuming ptr to chunk data
0xb94a5280 sz:0x0000003c, flags:...., name:struct tevent_context
Você pode confirmar que ele foi encontrado depois usando o tcinfo.
(gdb) tcinfo
[libtalloc] null_context: 0xb94a5028
[libtalloc] Version: 2.0.7
[libtalloc] File: /usr/lib/i386-linux-gnu/libtalloc.so.2.0.7
Agora que o null_context está definido, você pode executar outros comandos que normalmente reclamariam que ele não estava definido, como o comando tcsearch.
O tcchunk pode fornecer um resumo do chunk, uma saída mais detalhada de cada campo ou informações extremamente detalhadas sobre cada chunk ao redor.
NOTA: Uma coisa importante a observar sobre o tcchunk é que, internamente, ele usa o método tc_chunk(), que tenta corrigir erros cometidos ao informar o endereço do chunk. Especificamente, se você passar o endereço dos dados do chunk em si, e ele não encontrar a magic esperada do talloc, ele procurará um cabeçalho de chunk legítimo um pouco antes na memória. Isso pode atrapalhar em cenários de corrupção, então certifique-se sempre de estar passando o endereço explícito, a menos que esteja fazendo uma análise superficial.
Saída resumida:
(gdb) tcchunk 0x80a13c88
0x80a13c88 sz:0x00000020, flags:..p., name:struct netr_ServerPasswordSet
A seguir está uma legenda para os chunks na saída resumida:
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)
Saída detalhada:
(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
Os chunks do talloc contêm alguns valores mágicos que podem ser usados para validar se são íntegros. O comando tcvalidate analisa um chunk para garantir que a magic do chunk seja a esperada. Além disso, ele analisa todos os outros membros ponteiro para garantir que realmente caiam em faixas de memória (conhecidas pelo gdb), se o tamanho é válido, etc.
(gdb) tcvalidate 0x80a13c88
Chunk header is valid
Usaremos um método embutido para modificar um valor e mostrar como isso poderia falhar:
(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
O tcparents pode ser usado para ver todos os pais do chunk fornecido:
(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
O tchildren pode ser usado para ver todos os filhos (e netos, etc.) do chunk fornecido: