
Heap analysis tooling for mempool
Note préliminaire : nous vous recommandons de l'utiliser dans le cadre de asatools, mais il peut également être utilisé de manière autonome.
libmempool est un script Python et un outil d'analyse GDB destiné à faciliter l'analyse
des structures de données liées aux mempools trouvées dans différents tas des appareils Cisco ASA.
Normalement, ces informations sont intégrées dans un chunk du tas, ou constituent une
extension personnalisée d'une autre structure de tas courante, telle qu'une structure
mstate de dlmalloc.
Cisco utilise le terme mempool pour décrire des régions de mémoire mappées à diverses fins, telles que les allocations générales, la DMA, etc. Ces régions contiennent généralement leur propre tas, comme dlmalloc. Les routines d'allocation sur ces mempools sont généralement effectuées à l'aide de wrappers autour de l'allocateur de tas sous-jacent, et ces wrappers injectent des métadonnées spécifiques aux mempools dans les allocations résultantes. Nous désignons ces métadonnées sous le nom d'en-tête de mempool, ou mh en abrégé. De même, nous désignerons régulièrement un mempool par mp. L'utilisation de mh pour décrire un en-tête de mempool est également cohérente avec les différentes chaînes trouvées dans les binaires lina de Cisco ASA.
Bien que libmempool puisse être utilisé comme outil autonome pour analyser certains éléments liés aux en-têtes de mempool et aux structures de données, sa plus grande valeur vient de son utilisation en tant que callback provenant d'autres bibliothèques telles que libdlmalloc ou libptmalloc.
Il convient de noter que certains aspects du mempool, tels que les bins utilisés pour suivre les chunks en cours d'utilisation, sont intégrés dans une structure mstate de dlmalloc 2.8.x et suivent le même dimensionnement des bins. Cela signifie que, presque inévitablement, vous devrez fouiller au moins la mstate qui encapsule les données du mempool à l'aide de libdlmalloc.
libmempool a été testé avec les versions 32 bits / 64 bits de Cisco ASA (séries ASA5500-X et GNS3) qui utilisent dlmalloc2.8 ou l'allocateur ptmalloc2 de glibc. Il a été testé sur de nombreuses versions d'ASA, notamment de nombreuses branches 8.x.y et 9.x.y. Cependant, il est tout à fait possible qu'il ne fonctionne pas sur certaines versions.
Pour utiliser libmempool de manière autonome, il vous suffit d'importer le fichier libmempool.py
dans votre projet. Cela vous permet d'effectuer certaines actions limitées, comme
enregistrer l'objet mpcallback, etc. Cela peut être utile si vous effectuez une
analyse hors ligne des fonctionnalités de tas journalisées.
Pour l'importer dans GDB, le script nécessite simplement GDB avec la prise en charge de Python. Bien que la plupart des versions modernes de GDB soient passées à Python 3, certaines attendent encore Python 2.7. Le script a été testé sur les deux, mais le développement et les tests sont principalement effectués avec Python 3.
(gdb) source libmempool_gdb.py
Nous séparons l'essentiel de la logique liée à GDB de libmempool.py dans libmempool_gdb.py afin de tester l'abstraction des choses et pour que vous puissiez facilement utiliser libmempool.py en dehors de GDB. Cela changera probablement à l'avenir, car nous voudrons éventuellement implémenter des abstractions de moteur de débogage similaires à celles utilisées par d'autres outils d'analyse de tas comme libheap et shadow.
Bien qu'une grande partie de la valeur de libmempool provienne de la fonction de rappel mpcallback
qu'il expose, il existe un certain nombre de commandes GDB intégrées que nous pouvons examiner.
(gdb) mphelp
[libmempool] mempool commands for gdb
[libmempool] mpheader -v -x <addr> : show chunk contents (-v for verbose, -x for data dump)
[libmempool] mpbinwalk [-v] [-p <addr>] <sz> : walk an mpbin and operate on each chunk in a bin
[libmempool] mpbin <addr> : determine to which bin an mp_header is associated to
[libmempool] mpmstate <addr> : display and cache a mempool mstate address
[libmempool] mphelp
En supposant que nous connaissions l'adresse d'un en-tête de mempool, nous pouvons analyser ses données. Notez qu'il doit s'agir de l'adresse de l'en-tête de mempool lui-même, et non de l'adresse des métadonnées de chunk de l'allocateur central. Ainsi, nous pouvons vider le contenu comme suit :
(gdb) mpheader 0x7fffbc1c1ca0
struct mp_header @ 0x7fffbc1c1ca0 {
mh_magic = 0xa11c0123
mh_len = 0x3
mh_refcount = 0x10000
mh_unused = 0x0
mh_fd_link = 0x7fffbc1c19e0 (OK)
mh_bk_link = 0x7ffff7ff7540 (-)
alloc_pc = 0x55555849e260 (-)
free_pc = 0x0 (-)
Nous pouvons également vider le contenu hexadécimal du chunk en utilisant -x.
(gdb) mpheader -x 0x7fffbc1c1ca0
struct mp_header @ 0x7fffbc1c1ca0 {
mh_magic = 0xa11c0123
mh_len = 0x3
mh_refcount = 0x10000
mh_unused = 0x0
mh_fd_link = 0x7fffbc1c19e0 (OK)
mh_bk_link = 0x7ffff7ff7540 (-)
alloc_pc = 0x55555849e260 (-)
free_pc = 0x0 (-)
0x3 bytes of chunk data:
0x7fffbc1c1cd0: 0x55 0x04 0x03
Les mempools ont le concept de bins, qui sont des listes doublement chaînées de mêmes tailles
que les bins de dlmalloc, mais qui sont utilisées pour suivre les chunks en cours d'utilisation plutôt
que les chunks libres. Cela est fait pour la comptabilité de l'utilisation de la mémoire sur les équipements Cisco.
Il arrive souvent que vous trouviez un chunk contenant un en-tête de mempool, mais que vous
ne sachiez pas encore où se trouve la structure mstate dans le tas. Pour vous
repérer, vous pouvez utiliser la commande mpbin, qui vous donnera l'adresse du
bin de mempool dans lequel se trouve actuellement un chunk en cours d'utilisation. Par exemple :
(gdb) mpbin 0x7fffbc1c1ca0
[libmempool] Found bin start at 0x7ffff7ff7540
[libmempool] Cached new mp_mstate @ 0x7ffff7ff73c0
[libmempool] mp_smallbin[08] - sz: 0x00000040 cnt: 0x00d3, mh_fd_link: 0x7fffbc1c1ca0
Cette commande a non seulement trouvé le début de la partie mempool de la mstate, mais elle l'a aussi
mise en cache et répertorie le bin spécifique dans lequel se trouve le chunk. Nous pouvons
éventuellement utiliser la commande mpbinwalk pour lister les 0xd3 chunks de ce bin,
ou seulement jusqu'à un nombre spécifique :
(gdb) mpbinwalk 0x40
[libmempool] mp_header @ 0x7ffff7ff7540 - mh_len: 0x00000000, alloc_pc: 0x00000000 [BIN HEAD]
[libmempool] mp_header @ 0x7fffbc1c1ca0 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[libmempool] mp_header @ 0x7fffbc1c19e0 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[libmempool] mp_header @ 0x7fffbc1c1750 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[libmempool] mp_header @ 0x7fffbc1bffa0 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[libmempool] mp_header @ 0x7fffbc1bff60 - mh_len: 0x00000003, alloc_pc: 0x5555584a1288
[libmempool] mp_header @ 0x7fffbc1c0050 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[...]
Maintenant, si nous voulons vider toute la partie mempool de la structure mstate (c'est-à-dire
tous les bins et les statistiques associées), nous pouvons utiliser la commande mpmstate :
(gdb) mpmstate 0x7ffff7ff73c0
struct mp_mstate @ 0x7ffff7ff73c0 {
mp_smallbin[00] - sz: 0x00000000 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[01] - sz: 0x00000008 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[02] - sz: 0x00000010 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[03] - sz: 0x00000018 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[04] - sz: 0x00000020 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[05] - sz: 0x00000028 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[06] - sz: 0x00000030 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[07] - sz: 0x00000038 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[08] - sz: 0x00000040 cnt: 0x00d3, mh_fd_link: 0x7fffbc1c1ca0
mp_smallbin[09] - sz: 0x00000048 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[10] - sz: 0x00000050 cnt: 0x0429, mh_fd_link: 0x7fffa4000d10
mp_smallbin[11] - sz: 0x00000058 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[12] - sz: 0x00000060 cnt: 0x335d, mh_fd_link: 0x7fffb80064b0
mp_smallbin[13] - sz: 0x00000068 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[14] - sz: 0x00000070 cnt: 0x073a, mh_fd_link: 0x7fffbc1c7260
mp_smallbin[15] - sz: 0x00000078 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[16] - sz: 0x00000080 cnt: 0x0301, mh_fd_link: 0x7fffbc1c72d0
mp_smallbin[17] - sz: 0x00000088 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[18] - sz: 0x00000090 cnt: 0x0c98, mh_fd_link: 0x7fffc9488920
mp_smallbin[19] - sz: 0x00000098 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[20] - sz: 0x000000a0 cnt: 0x0161, mh_fd_link: 0x7fffa40009a0
mp_smallbin[21] - sz: 0x000000a8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[22] - sz: 0x000000b0 cnt: 0x0092, mh_fd_link: 0x7fffa4000a40
mp_smallbin[23] - sz: 0x000000b8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[24] - sz: 0x000000c0 cnt: 0x0120, mh_fd_link: 0x7fffbc1c1270
mp_smallbin[25] - sz: 0x000000c8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[26] - sz: 0x000000d0 cnt: 0x00ba, mh_fd_link: 0x7fffbc1b51a0
mp_smallbin[27] - sz: 0x000000d8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[28] - sz: 0x000000e0 cnt: 0x005b, mh_fd_link: 0x7fffc9485020
mp_smallbin[29] - sz: 0x000000e8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[30] - sz: 0x000000f0 cnt: 0x016e, mh_fd_link: 0x7fffbc1c7040
mp_smallbin[31] - sz: 0x000000f8 cnt: 0x0000, mh_fd_link: 0x0
mp_treebin[00] - sz: 0x00000180 cnt: 0x050f, mh_fd_link: 0x7fffbc1c7130
mp_treebin[01] - sz: 0x00000200 cnt: 0x00ce, mh_fd_link: 0x7fffbc1c2610
mp_treebin[02] - sz: 0x00000300 cnt: 0x011a, mh_fd_link: 0x7fffa40053c0
mp_treebin[03] - sz: 0x00000400 cnt: 0x013e, mh_fd_link: 0x7fffa4000f80
mp_treebin[04] - sz: 0x00000600 cnt: 0x016f, mh_fd_link: 0x7fffbc1c2a80
mp_treebin[05] - sz: 0x00000800 cnt: 0x009a, mh_fd_link: 0x7fffbc1b4880
mp_treebin[06] - sz: 0x00000c00 cnt: 0x0081, mh_fd_link: 0x7fffbc1b38b0
mp_treebin[07] - sz: 0x00001000 cnt: 0x002b, mh_fd_link: 0x7fffbc1b2330
mp_treebin[08] - sz: 0x00001800 cnt: 0x0326, mh_fd_link: 0x7fffc9368e10
mp_treebin[09] - sz: 0x00002000 cnt: 0x0038, mh_fd_link: 0x7fffc92e9c60
mp_treebin[10] - sz: 0x00003000 cnt: 0x00b9, mh_fd_link: 0x7fffa4003380
mp_treebin[11] - sz: 0x00004000 cnt: 0x006d, mh_fd_link: 0x7fffc9457720
mp_treebin[12] - sz: 0x00006000 cnt: 0x023b, mh_fd_link: 0x7fffbc1ac000
mp_treebin[13] - sz: 0x00008000 cnt: 0x0018, mh_fd_link: 0x7fffc946f850
mp_treebin[14] - sz: 0x0000c000 cnt: 0x0030, mh_fd_link: 0x7fffc935fcc0
mp_treebin[15] - sz: 0x00010000 cnt: 0x0019, mh_fd_link: 0x7fffb44262b0
mp_treebin[16] - sz: 0x00018000 cnt: 0x0073, mh_fd_link: 0x7fffc93fe020
mp_treebin[17] - sz: 0x00020000 cnt: 0x0018, mh_fd_link: 0x7fffc94368f0
mp_treebin[18] - sz: 0x00030000 cnt: 0x000c, mh_fd_link: 0x7fffb0665010
mp_treebin[19] - sz: 0x00040000 cnt: 0x001c, mh_fd_link: 0x7fffa836d010
mp_treebin[20] - sz: 0x00060000 cnt: 0x000d, mh_fd_link: 0x7fffa8c55010
mp_treebin[21] - sz: 0x00080000 cnt: 0x001d, mh_fd_link: 0x7fffa9009010
mp_treebin[22] - sz: 0x000c0000 cnt: 0x0006, mh_fd_link: 0x7fffa9081010
mp_treebin[23] - sz: 0x00100000 cnt: 0x000a, mh_fd_link: 0x7fffb025f010
mp_treebin[24] - sz: 0x00180000 cnt: 0x000b, mh_fd_link: 0x7fffa8b38010
mp_treebin[25] - sz: 0x00200000 cnt: 0x000e, mh_fd_link: 0x7fffb008a010
mp_treebin[26] - sz: 0x00300000 cnt: 0x0007, mh_fd_link: 0x7fffb0323010
mp_treebin[27] - sz: 0x00400000 cnt: 0x0002, mh_fd_link: 0x7fffa87f5010
mp_treebin[28] - sz: 0x00600000 cnt: 0x0003, mh_fd_link: 0x7fffa83e9010
mp_treebin[29] - sz: 0x00800000 cnt: 0x0001, mh_fd_link: 0x7fffab328010
mp_treebin[30] - sz: 0x00c00000 cnt: 0x0001, mh_fd_link: 0x7fffd706d010
mp_treebin[31] - sz: 0xffffffff cnt: 0x0003, mh_fd_link: 0x7fffa9326010 [UNSORTED]
Nous implémentons dans libmempool un callback « aware » de dlmalloc et de ptmalloc, destiné à être appelé par libdlmalloc ou libptmalloc pour enrichir leur annotation de chunk avec des données supplémentaires spécifiques aux mempools. Par exemple, si libdlmalloc est utilisé pour afficher une liste détaillée d'un chunk dlmalloc, et que ce chunk contient en son sein un en-tête de mempool, alors le callback mempool l'affichera.
Pour plus d'informations sur ce à quoi cela ressemble lorsqu'il est utilisé via d'autres outils, veuillez consulter les fichiers README de libdlmalloc et libptmalloc.
Nous ajouterons probablement des fonctionnalités à libmempool selon nos besoins ou lors de futures recherches sur Cisco ASA. Les ajouts actuellement prévus sont :
Nous serions ravis d'avoir des retours sur cet outil et nous acceptons volontiers les pull requests.
Aaron Adams
aaron<dot>adams<at>nccgroup<dot>trustCedric Halbronn
cedric<dot>halbronn<at>nccgroup<dot>trust