Les sections suivantes incluent des types courants de plantage natif, une analyse d'un exemple de vidage de plantage et une discussion sur les tombstones. Chaque type de plantage inclut un exemple de sortie debuggerd
avec des éléments clés mis en évidence pour vous aider à distinguer le type de plantage spécifique.
Abandonner
Les arrêts sont intéressants, car ils sont délibérés. Il existe de nombreuses façons d'avorter (y compris en appelant abort(3)
, en échouant à une assert(3)
ou en utilisant l'un des types de journalisation fatale spécifiques à Android), mais tous impliquent d'appeler abort
. Un appel à abort
signale le thread appelant avec SIGABRT. Par conséquent, un frame affichant "abort" dans libc.so
et SIGABRT sont les éléments à rechercher dans la sortie debuggerd
pour reconnaître ce cas.
Il peut y avoir une ligne "message d'abandon" explicite. Vous devez également consulter la sortie logcat
pour voir ce que ce thread a enregistré avant de s'arrêter délibérément, car contrairement à assert(3)
ou aux fonctionnalités de journalisation fatales de haut niveau, abort(3)
n'accepte pas de message.
Les versions actuelles d'Android insèrent l'appel système tgkill(2)
en ligne. Leurs piles sont donc les plus faciles à lire, avec l'appel à abort(3) tout en haut:
pid: 4637, tid: 4637, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'some_file.c:123: some_function: assertion "false" failed' r0 00000000 r1 0000121d r2 00000006 r3 00000008 r4 0000121d r5 0000121d r6 ffb44a1c r7 0000010c r8 00000000 r9 00000000 r10 00000000 r11 00000000 ip ffb44c20 sp ffb44a08 lr eace2b0b pc eace2b16 backtrace: #00 pc 0001cb16 /system/lib/libc.so (abort+57) #01 pc 0001cd8f /system/lib/libc.so (__assert2+22) #02 pc 00001531 /system/bin/crasher (do_action+764) #03 pc 00002301 /system/bin/crasher (main+68) #04 pc 0008a809 /system/lib/libc.so (__libc_init+48) #05 pc 00001097 /system/bin/crasher (_start_main+38)
Les anciennes versions d'Android suivaient un chemin complexe entre l'appel d'interruption d'origine (image 4 ici) et l'envoi réel du signal (image 0 ici).
Cela était particulièrement vrai sur ARM 32 bits, qui a ajouté __libc_android_abort
(cadre 3 ici) à la séquence raise
/pthread_kill
/tgkill
des autres plates-formes:
pid: 1656, tid: 1656, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'some_file.c:123: some_function: assertion "false" failed' r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8 r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010 backtrace: #00 pc 00042c98 /system/lib/libc.so (tgkill+12) #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32) #02 pc 0001bb87 /system/lib/libc.so (raise+10) #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34) #04 pc 000168e8 /system/lib/libc.so (abort+4) #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16) #06 pc 00018d35 /system/lib/libc.so (__assert2+20) #07 pc 00000f21 /system/xbin/crasher #08 pc 00016795 /system/lib/libc.so (__libc_init+44) #09 pc 00000abc /system/xbin/crasher
Vous pouvez reproduire un cas de ce type de plantage à l'aide de crasher
abort
.
Déréférencement pur du pointeur nul
Il s'agit du plantage natif classique. Bien qu'il ne s'agisse que d'un cas particulier du type de plantage suivant, il est intéressant de le mentionner séparément, car il nécessite généralement le moins de réflexion.
Dans l'exemple ci-dessous, même si la fonction qui plante se trouve dans libc.so
, comme les fonctions de chaîne ne fonctionnent que sur les pointeurs qui leur sont donnés, vous pouvez en déduire que strlen(3)
a été appelé avec un pointeur nul. Ce plantage doit être directement attribué à l'auteur du code appelant. Dans ce cas, le frame 01 est l'appelant incorrect.
pid: 25326, tid: 25326, name: crasher >>> crasher <<< signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 r0 00000000 r1 00000000 r2 00004c00 r3 00000000 r4 ab088071 r5 fff92b34 r6 00000002 r7 fff92b40 r8 00000000 r9 00000000 sl 00000000 fp fff92b2c ip ab08cfc4 sp fff92a08 lr ab087a93 pc efb78988 cpsr 600d0030 backtrace: #00 pc 00019988 /system/lib/libc.so (strlen+71) #01 pc 00001a8f /system/xbin/crasher (strlen_null+22) #02 pc 000017cd /system/xbin/crasher (do_action+948) #03 pc 000020d5 /system/xbin/crasher (main+100) #04 pc 000177a1 /system/lib/libc.so (__libc_init+48) #05 pc 000010e4 /system/xbin/crasher (_start+96)
Vous pouvez reproduire un cas de ce type de plantage à l'aide de crasher
strlen-NULL
.
Déréférencement du pointeur nul à basse adresse
Dans de nombreux cas, l'adresse de l'erreur n'est pas 0, mais un autre nombre bas. Les adresses à deux ou trois chiffres en particulier sont très courantes, tandis qu'une adresse à six chiffres n'est presque certainement pas une déréférence de pointeur nul, ce qui nécessiterait un décalage de 1 Mo. Cela se produit généralement lorsque le code désérialise un pointeur nul comme s'il s'agissait d'une structure valide. Les fonctions courantes sont fprintf(3)
(ou toute autre fonction prenant un FILE*) et readdir(3)
, car le code ne vérifie souvent pas que l'appel fopen(3)
ou opendir(3)
a bien réussi en premier.
Voici un exemple de readdir
:
pid: 25405, tid: 25405, name: crasher >>> crasher <<< signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc r0 0000000c r1 00000000 r2 00000000 r3 3d5f0000 r4 00000000 r5 0000000c r6 00000002 r7 ff8618f0 r8 00000000 r9 00000000 sl 00000000 fp ff8618dc ip edaa6834 sp ff8617a8 lr eda34a1f pc eda618f6 cpsr 600d0030 backtrace: #00 pc 000478f6 /system/lib/libc.so (pthread_mutex_lock+1) #01 pc 0001aa1b /system/lib/libc.so (readdir+10) #02 pc 00001b35 /system/xbin/crasher (readdir_null+20) #03 pc 00001815 /system/xbin/crasher (do_action+976) #04 pc 000021e5 /system/xbin/crasher (main+100) #05 pc 000177a1 /system/lib/libc.so (__libc_init+48) #06 pc 00001110 /system/xbin/crasher (_start+96)
Ici, la cause directe du plantage est que pthread_mutex_lock(3)
a tenté d'accéder à l'adresse 0xc (trame 0). Mais la première chose que pthread_mutex_lock
fait est de désadresser l'élément state
de l'pthread_mutex_t*
qui lui a été donné. Si vous examinez la source, vous pouvez voir que cet élément se trouve à l'offset 0 dans la structure, ce qui vous indique que pthread_mutex_lock
a reçu le pointeur non valide 0xc. À partir du frame 1, vous pouvez voir qu'il a été attribué par readdir
, qui extrait le champ mutex_
du DIR*
qui lui est donné. En examinant cette structure, vous pouvez voir que mutex_
se trouve au décalage sizeof(int) + sizeof(size_t) + sizeof(dirent*)
dans struct DIR
, qui sur un appareil 32 bits est 4 + 4 + 4 = 12 = 0xc. Vous avez donc trouvé le bug: readdir
a reçu un pointeur nul de l'appelant. À ce stade, vous pouvez coller la pile dans l'outil de pile pour savoir où cela s'est produit dans logcat.
struct DIR { int fd_; size_t available_bytes_; dirent* next_; pthread_mutex_t mutex_; dirent buff_[15]; long current_pos_; };
Dans la plupart des cas, vous pouvez ignorer cette analyse. Une adresse de défaut suffisamment basse signifie généralement que vous pouvez simplement ignorer les frames libc.so
de la pile et accuser directement le code appelant. Mais ce n'est pas toujours le cas. Voici comment présenter un argumentaire convaincant.
Vous pouvez reproduire des cas de ce type de plantage à l'aide de crasher
fprintf-NULL
ou de crasher readdir-NULL
.
Échec de FORTIFY
Une erreur FORTIFY est un cas particulier d'interruption qui se produit lorsque la bibliothèque C détecte un problème pouvant entraîner une faille de sécurité. De nombreuses fonctions de la bibliothèque C sont fortifiées. Elles prennent un argument supplémentaire qui leur indique la taille réelle d'un tampon et vérifient au moment de l'exécution si l'opération que vous essayez d'effectuer est adaptée. Voici un exemple où le code tente de read(fd, buf, 32)
dans un tampon qui ne fait en réalité que 10 octets de long :
pid: 25579, tid: 25579, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'FORTIFY: read: prevented 32-byte write into 10-byte buffer' r0 00000000 r1 000063eb r2 00000006 r3 00000008 r4 ff96f350 r5 000063eb r6 000063eb r7 0000010c r8 00000000 r9 00000000 sl 00000000 fp ff96f49c ip 00000000 sp ff96f340 lr ee83ece3 pc ee86ef0c cpsr 000d0010 backtrace: #00 pc 00049f0c /system/lib/libc.so (tgkill+12) #01 pc 00019cdf /system/lib/libc.so (abort+50) #02 pc 0001e197 /system/lib/libc.so (__fortify_fatal+30) #03 pc 0001baf9 /system/lib/libc.so (__read_chk+48) #04 pc 0000165b /system/xbin/crasher (do_action+534) #05 pc 000021e5 /system/xbin/crasher (main+100) #06 pc 000177a1 /system/lib/libc.so (__libc_init+48) #07 pc 00001110 /system/xbin/crasher (_start+96)
Vous pouvez reproduire un cas de ce type de plantage à l'aide de crasher
fortify
.
Corruption de la pile détectée par -fstack-protector
L'option -fstack-protector
du compilateur insère des vérifications dans les fonctions avec des tampons sur la pile pour éviter les dépassements de tampon. Cette option est activée par défaut pour le code de la plate-forme, mais pas pour les applications. Lorsque cette option est activée, le compilateur ajoute des instructions au prologue de la fonction pour écrire une valeur aléatoire juste après le dernier local sur la pile et à l'épilogue de la fonction pour la lire et vérifier qu'elle n'a pas changé. Si cette valeur a changé, elle a été écrasée par un débordement de tampon. L'épilogue appelle donc __stack_chk_fail
pour consigner un message et s'arrêter.
pid: 26717, tid: 26717, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'stack corruption detected' r0 00000000 r1 0000685d r2 00000006 r3 00000008 r4 ffd516d8 r5 0000685d r6 0000685d r7 0000010c r8 00000000 r9 00000000 sl 00000000 fp ffd518bc ip 00000000 sp ffd516c8 lr ee63ece3 pc ee66ef0c cpsr 000e0010 backtrace: #00 pc 00049f0c /system/lib/libc.so (tgkill+12) #01 pc 00019cdf /system/lib/libc.so (abort+50) #02 pc 0001e07d /system/lib/libc.so (__libc_fatal+24) #03 pc 0004863f /system/lib/libc.so (__stack_chk_fail+6) #04 pc 000013ed /system/xbin/crasher (smash_stack+76) #05 pc 00001591 /system/xbin/crasher (do_action+280) #06 pc 00002219 /system/xbin/crasher (main+100) #07 pc 000177a1 /system/lib/libc.so (__libc_init+48) #08 pc 00001144 /system/xbin/crasher (_start+96)
Vous pouvez distinguer cela des autres types d'abandon par la présence de __stack_chk_fail
dans la trace arrière et le message d'abandon spécifique.
Vous pouvez reproduire un cas de ce type de plantage à l'aide de crasher
smash-stack
.
Seccomp SIGSYS à partir d'un appel système non autorisé
Le système seccomp (plus précisément seccomp-bpf) limite l'accès aux appels système. Pour en savoir plus sur seccomp pour les développeurs de plate-forme, consultez l'article de blog Filtre seccomp dans Android O. Un thread qui appelle un appel système limité reçoit un signal SIGSYS avec le code SYS_SECCOMP. Le numéro d'appel système s'affiche dans la ligne de cause, avec l'architecture. Il est important de noter que les numéros d'appel système varient selon les architectures. Par exemple, l'appel système readlinkat(2)
est le numéro 305 sur x86, mais le numéro 267 sur x86-64.
Le numéro d'appel est à nouveau différent sur arm et arm64. Étant donné que les numéros d'appel système varient d'une architecture à l'autre, il est généralement plus facile d'utiliser la trace de la pile pour identifier l'appel système refusé plutôt que de rechercher le numéro d'appel système dans les en-têtes.
pid: 11046, tid: 11046, name: crasher >>> crasher <<< signal 31 (SIGSYS), code 1 (SYS_SECCOMP), fault addr -------- Cause: seccomp prevented call to disallowed arm system call 99999 r0 cfda0444 r1 00000014 r2 40000000 r3 00000000 r4 00000000 r5 00000000 r6 00000000 r7 0001869f r8 00000000 r9 00000000 sl 00000000 fp fffefa58 ip fffef898 sp fffef888 lr 00401997 pc f74f3658 cpsr 600f0010 backtrace: #00 pc 00019658 /system/lib/libc.so (syscall+32) #01 pc 00001993 /system/bin/crasher (do_action+1474) #02 pc 00002699 /system/bin/crasher (main+68) #03 pc 0007c60d /system/lib/libc.so (__libc_init+48) #04 pc 000011b0 /system/bin/crasher (_start_main+72)
Vous pouvez distinguer les appels système non autorisés des autres plantages par la présence de SYS_SECCOMP
sur la ligne de signal et de la description sur la ligne de cause.
Vous pouvez reproduire un cas de ce type de plantage à l'aide de crasher
seccomp
.
Non-respect de la mémoire réservée à l'exécution (Android 10 uniquement)
Pour arm64 dans Android 10 uniquement, les segments exécutables des binaires et des bibliothèques ont été mappés dans la mémoire réservée à l'exécution (non lisible) en tant que technique de renforcement contre les attaques de réutilisation de code. Cette atténuation interagit mal avec d'autres atténuations et a été supprimée par la suite.
Rendre le code illisible provoque des lectures intentionnelles et involontaires dans les segments de mémoire marqués comme "exécuter uniquement" pour générer une SIGSEGV
avec le code SEGV_ACCERR
. Cela peut se produire en raison d'un bug, d'une faille, de données mélangées à du code (comme un pool de littéraux) ou d'une introspection de la mémoire intentionnelle.
Le compilateur suppose que le code et les données ne sont pas mélangés, mais des problèmes peuvent survenir lors de l'assemblage manuel. Dans de nombreux cas, il suffit de déplacer les constantes vers une section .data
pour résoudre ces problèmes.
Si l'introspection du code est absolument nécessaire sur les sections de code exécutables, mprotect(2)
doit d'abord être appelé pour marquer le code comme lisible, puis à nouveau pour le marquer comme illisible une fois l'opération terminée.
pid: 2938, tid: 2940, name: crasher64 >>> crasher64 <<< signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x5f2ced24a8 Cause: execute-only (no-read) memory access error; likely due to data in .text. x0 0000000000000000 x1 0000005f2cecf21f x2 0000000000000078 x3 0000000000000053 x4 0000000000000074 x5 8000000000000000 x6 ff71646772607162 x7 00000020dcf0d16c x8 0000005f2ced24a8 x9 000000781251c55e x10 0000000000000000 x11 0000000000000000 x12 0000000000000014 x13 ffffffffffffffff x14 0000000000000002 x15 ffffffffffffffff x16 0000005f2ced52f0 x17 00000078125c0ed8 x18 0000007810e8e000 x19 00000078119fbd50 x20 00000078125d6020 x21 00000078119fbd50 x22 00000b7a00000b7a x23 00000078119fbdd8 x24 00000078119fbd50 x25 00000078119fbd50 x26 00000078119fc018 x27 00000078128ea020 x28 00000078119fc020 x29 00000078119fbcb0 sp 00000078119fba40 lr 0000005f2ced1b94 pc 0000005f2ced1ba4 backtrace: #00 pc 0000000000003ba4 /system/bin/crasher64 (do_action+2348) #01 pc 0000000000003234 /system/bin/crasher64 (thread_callback+44) #02 pc 00000000000e2044 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36) #03 pc 0000000000083de0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
Vous pouvez distinguer les violations de mémoire en mode "exécution uniquement" des autres plantages à l'aide de la ligne de cause.
Vous pouvez reproduire un cas de ce type de plantage à l'aide de crasher xom
.
Erreur détectée par fdsan
Le nettoyeur de descripteurs de fichiers fdsan d'Android permet de détecter les erreurs courantes liées aux descripteurs de fichiers, telles que l'utilisation après fermeture et la double fermeture. Pour en savoir plus sur le débogage (et l'évitement) de cette classe d'erreurs, consultez la documentation fdsan.
pid: 32315, tid: 32315, name: crasher64 >>> crasher64 <<< signal 35 (), code -1 (SI_QUEUE), fault addr -------- Abort message: 'attempted to close file descriptor 3, expected to be unowned, actually owned by FILE* 0x7d8e413018' x0 0000000000000000 x1 0000000000007e3b x2 0000000000000023 x3 0000007fe7300bb0 x4 3033313465386437 x5 3033313465386437 x6 3033313465386437 x7 3831303331346538 x8 00000000000000f0 x9 0000000000000000 x10 0000000000000059 x11 0000000000000034 x12 0000007d8ebc3a49 x13 0000007fe730077a x14 0000007fe730077a x15 0000000000000000 x16 0000007d8ec9a7b8 x17 0000007d8ec779f0 x18 0000007d8f29c000 x19 0000000000007e3b x20 0000000000007e3b x21 0000007d8f023020 x22 0000007d8f3b58dc x23 0000000000000001 x24 0000007fe73009a0 x25 0000007fe73008e0 x26 0000007fe7300ca0 x27 0000000000000000 x28 0000000000000000 x29 0000007fe7300c90 sp 0000007fe7300860 lr 0000007d8ec2f22c pc 0000007d8ec2f250 backtrace: #00 pc 0000000000088250 /bionic/lib64/libc.so (fdsan_error(char const*, ...)+384) #01 pc 0000000000088060 /bionic/lib64/libc.so (android_fdsan_close_with_tag+632) #02 pc 00000000000887e8 /bionic/lib64/libc.so (close+16) #03 pc 000000000000379c /system/bin/crasher64 (do_action+1316) #04 pc 00000000000049c8 /system/bin/crasher64 (main+96) #05 pc 000000000008021c /bionic/lib64/libc.so (_start_main)
Vous pouvez distinguer cela des autres types d'abandon par la présence de fdsan_error
dans la trace arrière et le message d'abandon spécifique.
Vous pouvez reproduire une instance de ce type de plantage à l'aide de crasher fdsan_file
ou de crasher fdsan_dir
.
Examiner les fichiers de vidage de mémoire
Si vous n'êtes pas en train d'examiner un plantage spécifique, la source de la plate-forme inclut un outil de test de debuggerd
appelé "crasher". Si vous mm
dans system/core/debuggerd/
, vous obtiendrez à la fois un crasher
et un crasher64
sur votre chemin (ce dernier vous permettant de tester les plantages 64 bits). Crasher peut planter de nombreuses façons intéressantes en fonction des arguments de ligne de commande que vous fournissez.
Utilisez crasher --help
pour afficher la sélection actuellement prise en charge.
Pour vous présenter les différentes parties d'un dump de plantage, examinons cet exemple de dump de plantage:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: 'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys' Revision: '0' ABI: 'arm' pid: 1656, tid: 1656, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'some_file.c:123: some_function: assertion "false" failed' r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8 r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010 backtrace: #00 pc 00042c98 /system/lib/libc.so (tgkill+12) #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32) #02 pc 0001bb87 /system/lib/libc.so (raise+10) #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34) #04 pc 000168e8 /system/lib/libc.so (abort+4) #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16) #06 pc 00018d35 /system/lib/libc.so (__assert2+20) #07 pc 00000f21 /system/xbin/crasher #08 pc 00016795 /system/lib/libc.so (__libc_init+44) #09 pc 00000abc /system/xbin/crasher Tombstone written to: /data/tombstones/tombstone_06 *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
La ligne d'astérisques avec des espaces est utile si vous recherchez des plantages natifs dans un journal. La chaîne "*** ***" apparaît rarement dans les journaux, sauf au début d'un plantage natif.
Build fingerprint: 'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys'
L'empreinte digitale vous permet d'identifier précisément la version sur laquelle le plantage s'est produit.
Cette valeur est identique à celle de la propriété système ro.build.fingerprint
.
Revision: '0'
La version fait référence au matériel plutôt qu'au logiciel. Cette option n'est généralement pas utilisée, mais elle peut vous aider à ignorer automatiquement les bugs connus comme étant causés par un matériel défectueux. Cette valeur est identique à celle de la propriété système ro.revision
.
ABI: 'arm'
L'ABI est arm, arm64, x86 ou x86-64. Cela est principalement utile pour le script stack
mentionné ci-dessus, afin qu'il sache quelle chaîne d'outils utiliser.
pid: 1656, tid: 1656, name: crasher >>> crasher <<<
Cette ligne identifie le thread spécifique du processus qui a planté. Dans ce cas, il s'agissait du thread principal du processus. L'ID du processus et l'ID du thread correspondent donc. Le premier nom est celui du thread, et le nom entouré de >>> et de <<< est celui du processus. Pour une application, le nom du processus est généralement le nom de package complet (par exemple, com.facebook.katana), ce qui est utile pour signaler des bugs ou essayer de trouver l'application dans Google Play. Le pid et le tid peuvent également être utiles pour trouver les lignes de journal pertinentes précédant le plantage.
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Cette ligne indique le signal (SIGABRT) reçu et plus d'informations sur la façon dont il a été reçu (SI_TKILL). Les signaux signalés par debuggerd
sont SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV et SIGTRAP. Les codes spécifiques au signal varient en fonction du signal spécifique.
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
Tous les plantages ne comportent pas de ligne de message d'abandon, mais les abandons en font partie. Cette information est automatiquement collectée à partir de la dernière ligne de la sortie logcat fatale pour ce pid/tid. En cas d'arrêt délibéré, elle est susceptible d'expliquer pourquoi le programme s'est arrêté.
r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8 r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010
Le dump de registre affiche le contenu des registres de processeur au moment où le signal a été reçu. (Cette section varie considérablement d'une ABI à l'autre.) Leur utilité dépend du plantage exact.
backtrace: #00 pc 00042c98 /system/lib/libc.so (tgkill+12) #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32) #02 pc 0001bb87 /system/lib/libc.so (raise+10) #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34) #04 pc 000168e8 /system/lib/libc.so (abort+4) #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16) #06 pc 00018d35 /system/lib/libc.so (__assert2+20) #07 pc 00000f21 /system/xbin/crasher #08 pc 00016795 /system/lib/libc.so (__libc_init+44) #09 pc 00000abc /system/xbin/crasher
La trace de retour indique l'emplacement du code au moment du plantage. La première colonne correspond au numéro de frame (correspondant au style de gdb, où le frame le plus profond est 0). Les valeurs PC sont relatives à l'emplacement de la bibliothèque partagée plutôt qu'à des adresses absolues. La colonne suivante indique le nom de la région mappée (qui est généralement une bibliothèque partagée ou un exécutable, mais qui ne l'est pas, par exemple, pour le code compilé JIT). Enfin, si des symboles sont disponibles, le symbole auquel la valeur PC correspond est affiché, ainsi que le décalage dans ce symbole en octets. Vous pouvez l'utiliser avec objdump(1)
pour trouver l'instruction d'assembleur correspondante.
Lire les tombstones
Tombstone written to: /data/tombstones/tombstone_06
Cela vous indique où debuggerd
a écrit des informations supplémentaires.
debuggerd
conserve jusqu'à 10 tombstones, en parcourant les numéros de 00 à 09 et en écrasant les tombstones existantes si nécessaire.
La pierre tombale contient les mêmes informations que le dump de plantage, ainsi que quelques informations supplémentaires. Par exemple, il inclut les rétrotraces pour tous les threads (et pas seulement le thread qui plante), les registres à virgule flottante, les vidages de pile bruts et les vidages de mémoire autour des adresses dans les registres. Plus utile encore, il inclut également une carte mémoire complète (similaire à /proc/pid/maps
). Voici un exemple annoté d'un plantage de processus ARM 32 bits:
memory map: (fault address prefixed with --->) --->ab15f000-ab162fff r-x 0 4000 /system/xbin/crasher (BuildId: b9527db01b5cf8f5402f899f64b9b121)
Deux points sont à noter ici. La première est que cette ligne est précédée de "--->". Les cartes sont particulièrement utiles lorsque votre plantage n'est pas simplement une déréférence de pointeur nul. Si l'adresse de défaut est petite, il s'agit probablement d'une variante d'une déréférence de pointeur nul. Sinon, examiner les cartes autour de l'adresse de l'erreur peut souvent vous donner un indice sur ce qui s'est passé. Voici quelques problèmes potentiels que vous pouvez identifier en consultant les cartes:
- Lit/écrit au-delà de la fin d'un bloc de mémoire.
- Lecture/écriture avant le début d'un bloc de mémoire.
- Tentatives d'exécution de code non exécutable.
- Fin de la pile.
- Tentation d'écrire dans le code (comme dans l'exemple ci-dessus).
Deuxième remarque : les fichiers exécutables et les fichiers de bibliothèques partagées affichent le BuildId (s'il est présent) sous Android 6.0 ou version ultérieure. Vous pouvez ainsi voir exactement quelle version de votre code a planté. Les binaires de plate-forme incluent un BuildId par défaut depuis Android 6.0. Le NDK r12 et les versions ultérieures transmettent également automatiquement -Wl,--build-id
au linker.
ab163000-ab163fff r-- 3000 1000 /system/xbin/crasher ab164000-ab164fff rw- 0 1000 f6c80000-f6d7ffff rw- 0 100000 [anon:libc_malloc]
Sur Android, le tas n'est pas nécessairement une seule région. Les régions de tas seront libellées [anon:libc_malloc]
.
f6d82000-f6da1fff r-- 0 20000 /dev/__properties__/u:object_r:logd_prop:s0 f6da2000-f6dc1fff r-- 0 20000 /dev/__properties__/u:object_r:default_prop:s0 f6dc2000-f6de1fff r-- 0 20000 /dev/__properties__/u:object_r:logd_prop:s0 f6de2000-f6de5fff r-x 0 4000 /system/lib/libnetd_client.so (BuildId: 08020aa06ed48cf9f6971861abf06c9d) f6de6000-f6de6fff r-- 3000 1000 /system/lib/libnetd_client.so f6de7000-f6de7fff rw- 4000 1000 /system/lib/libnetd_client.so f6dec000-f6e74fff r-x 0 89000 /system/lib/libc++.so (BuildId: 8f1f2be4b37d7067d366543fafececa2) (load base 0x2000) f6e75000-f6e75fff --- 0 1000 f6e76000-f6e79fff r-- 89000 4000 /system/lib/libc++.so f6e7a000-f6e7afff rw- 8d000 1000 /system/lib/libc++.so f6e7b000-f6e7bfff rw- 0 1000 [anon:.bss] f6e7c000-f6efdfff r-x 0 82000 /system/lib/libc.so (BuildId: d189b369d1aafe11feb7014d411bb9c3) f6efe000-f6f01fff r-- 81000 4000 /system/lib/libc.so f6f02000-f6f03fff rw- 85000 2000 /system/lib/libc.so f6f04000-f6f04fff rw- 0 1000 [anon:.bss] f6f05000-f6f05fff r-- 0 1000 [anon:.bss] f6f06000-f6f0bfff rw- 0 6000 [anon:.bss] f6f0c000-f6f21fff r-x 0 16000 /system/lib/libcutils.so (BuildId: d6d68a419dadd645ca852cd339f89741) f6f22000-f6f22fff r-- 15000 1000 /system/lib/libcutils.so f6f23000-f6f23fff rw- 16000 1000 /system/lib/libcutils.so f6f24000-f6f31fff r-x 0 e000 /system/lib/liblog.so (BuildId: e4d30918d1b1028a1ba23d2ab72536fc) f6f32000-f6f32fff r-- d000 1000 /system/lib/liblog.so f6f33000-f6f33fff rw- e000 1000 /system/lib/liblog.so
En règle générale, une bibliothèque partagée comporte trois entrées adjacentes. L'une est lisible et exécutable (code), l'autre est en lecture seule (données en lecture seule) et l'autre est en lecture-écriture (données modifiables). La première colonne indique les plages d'adresses pour le mappage, la deuxième les autorisations (dans le style ls(1)
Unix habituel), la troisième le décalage dans le fichier (en hexadécimal), la quatrième la taille de la région (en hexadécimal) et la cinquième le fichier (ou un autre nom de région).
f6f34000-f6f53fff r-x 0 20000 /system/lib/libm.so (BuildId: 76ba45dcd9247e60227200976a02c69b) f6f54000-f6f54fff --- 0 1000 f6f55000-f6f55fff r-- 20000 1000 /system/lib/libm.so f6f56000-f6f56fff rw- 21000 1000 /system/lib/libm.so f6f58000-f6f58fff rw- 0 1000 f6f59000-f6f78fff r-- 0 20000 /dev/__properties__/u:object_r:default_prop:s0 f6f79000-f6f98fff r-- 0 20000 /dev/__properties__/properties_serial f6f99000-f6f99fff rw- 0 1000 [anon:linker_alloc_vector] f6f9a000-f6f9afff r-- 0 1000 [anon:atexit handlers] f6f9b000-f6fbafff r-- 0 20000 /dev/__properties__/properties_serial f6fbb000-f6fbbfff rw- 0 1000 [anon:linker_alloc_vector] f6fbc000-f6fbcfff rw- 0 1000 [anon:linker_alloc_small_objects] f6fbd000-f6fbdfff rw- 0 1000 [anon:linker_alloc_vector] f6fbe000-f6fbffff rw- 0 2000 [anon:linker_alloc] f6fc0000-f6fc0fff r-- 0 1000 [anon:linker_alloc] f6fc1000-f6fc1fff rw- 0 1000 [anon:linker_alloc_lob] f6fc2000-f6fc2fff r-- 0 1000 [anon:linker_alloc] f6fc3000-f6fc3fff rw- 0 1000 [anon:linker_alloc_vector] f6fc4000-f6fc4fff rw- 0 1000 [anon:linker_alloc_small_objects] f6fc5000-f6fc5fff rw- 0 1000 [anon:linker_alloc_vector] f6fc6000-f6fc6fff rw- 0 1000 [anon:linker_alloc_small_objects] f6fc7000-f6fc7fff rw- 0 1000 [anon:arc4random _rsx structure] f6fc8000-f6fc8fff rw- 0 1000 [anon:arc4random _rs structure] f6fc9000-f6fc9fff r-- 0 1000 [anon:atexit handlers] f6fca000-f6fcafff --- 0 1000 [anon:thread signal stack guard page]
Depuis Android 5.0, la bibliothèque C nomme la plupart de ses régions mappées anonymes, ce qui réduit le nombre de régions mystérieuses.
f6fcb000-f6fccfff rw- 0 2000 [stack:5081]
Les régions nommées [stack:tid]
sont les piles des threads donnés.
f6fcd000-f702afff r-x 0 5e000 /system/bin/linker (BuildId: 84f1316198deee0591c8ac7f158f28b7) f702b000-f702cfff r-- 5d000 2000 /system/bin/linker f702d000-f702dfff rw- 5f000 1000 /system/bin/linker f702e000-f702ffff rw- 0 2000 f7030000-f7030fff r-- 0 1000 f7031000-f7032fff rw- 0 2000 ffcd7000-ffcf7fff rw- 0 21000 ffff0000-ffff0fff r-x 0 1000 [vectors]
L'affichage de [vector]
ou de [vdso]
dépend de l'architecture. ARM utilise [vector]
, tandis que toutes les autres architectures utilisent [vdso]
.