
Outil d'analyse du tas pour 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 :