Comment traquer une erreur de double free ou de corruption en C ++ avec gdb

Lorsque je lance mon programme C ++, il se bloque avec cette erreur.

* glibc détecté * ./load: double gratuit ou corruption (! prev): 0x0000000000c6ed50 ***

J’essaie de le retrouver en utilisant un relevé de cout, mais je trouve cela difficile. Gdb pourrait-il faciliter cela? Comment est-il fait?

Si vous utilisez glibc, vous pouvez définir la variable d’environnement MALLOC_CHECK_ sur 2 , cela entraînera l’utilisation par glibc d’une version tolérante aux erreurs de malloc , qui entraînera l’ MALLOC_CHECK_ votre programme au moment où le double libre est effectué.

Vous pouvez définir ceci à partir de gdb en utilisant la commande set environment MALLOC_CHECK_ 2 avant d’exécuter votre programme; le programme doit abandonner, avec l’appel free() visible dans le backtrace.

voir la page de manuel de malloc() pour plus d’informations

Il existe au moins deux situations possibles:

  1. vous supprimez deux fois la même entité
  2. vous supprimez quelque chose qui n’a pas été alloué

Pour le premier, je suggère fortement NULL-ing tous les pointeurs supprimés.

Vous avez trois options:

  1. surcharger nouveau et supprimer et suivre les allocations
  2. oui, utilisez gdb – alors vous aurez une trace de votre crash, et ce sera probablement très utile
  3. comme suggéré – utilisez Valgrind – il n’est pas facile d’entrer, mais cela vous fera économiser mille fois plus à l’avenir …

Vous pouvez utiliser gdb, mais je voudrais d’abord essayer Valgrind . Voir le guide de démarrage rapide .

Trois règles de base:

  1. Définir le pointeur sur NULL après avoir libéré
  2. Vérifiez NULL avant de libérer.
  3. Initialiser le pointeur sur NULL au début.

La combinaison de ces trois fonctionne très bien.

Vous pouvez utiliser valgrind pour le déboguer.

 #include #include int main() { char *x = malloc(100); free(x); free(x); return 0; } [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1 [sand@PS-CNTOS-64-S11 testbox]$ ./t1 *** glibc detected *** ./t1: double free or corruption (top): 0x00000000058f7010 *** ======= Backtrace: ========= /lib64/libc.so.6[0x3a3127245f] /lib64/libc.so.6(cfree+0x4b)[0x3a312728bb] ./t1[0x400500] /lib64/libc.so.6(__libc_start_main+0xf4)[0x3a3121d994] ./t1[0x400429] ======= Memory map: ======== 00400000-00401000 r-xp 00000000 68:02 30246184 /home/sand/testbox/t1 00600000-00601000 rw-p 00000000 68:02 30246184 /home/sand/testbox/t1 058f7000-05918000 rw-p 058f7000 00:00 0 [heap] 3a30e00000-3a30e1c000 r-xp 00000000 68:03 5308733 /lib64/ld-2.5.so 3a3101b000-3a3101c000 r--p 0001b000 68:03 5308733 /lib64/ld-2.5.so 3a3101c000-3a3101d000 rw-p 0001c000 68:03 5308733 /lib64/ld-2.5.so 3a31200000-3a3134e000 r-xp 00000000 68:03 5310248 /lib64/libc-2.5.so 3a3134e000-3a3154e000 ---p 0014e000 68:03 5310248 /lib64/libc-2.5.so 3a3154e000-3a31552000 r--p 0014e000 68:03 5310248 /lib64/libc-2.5.so 3a31552000-3a31553000 rw-p 00152000 68:03 5310248 /lib64/libc-2.5.so 3a31553000-3a31558000 rw-p 3a31553000 00:00 0 3a41c00000-3a41c0d000 r-xp 00000000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1 3a41c0d000-3a41e0d000 ---p 0000d000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1 3a41e0d000-3a41e0e000 rw-p 0000d000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1 2b1912300000-2b1912302000 rw-p 2b1912300000 00:00 0 2b191231c000-2b191231d000 rw-p 2b191231c000 00:00 0 7ffffe214000-7ffffe229000 rw-p 7ffffffe9000 00:00 0 [stack] 7ffffe2b0000-7ffffe2b4000 r-xp 7ffffe2b0000 00:00 0 [vdso] ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0 [vsyscall] Aborted [sand@PS-CNTOS-64-S11 testbox]$ [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1 [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck ./t1 ==20859== Memcheck, a memory error detector ==20859== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==20859== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info ==20859== Command: ./t1 ==20859== ==20859== Invalid free() / delete / delete[] ==20859== at 0x4A05A31: free (vg_replace_malloc.c:325) ==20859== by 0x4004FF: main (t1.c:8) ==20859== Address 0x4c26040 is 0 bytes inside a block of size 100 free'd ==20859== at 0x4A05A31: free (vg_replace_malloc.c:325) ==20859== by 0x4004F6: main (t1.c:7) ==20859== ==20859== ==20859== HEAP SUMMARY: ==20859== in use at exit: 0 bytes in 0 blocks ==20859== total heap usage: 1 allocs, 2 frees, 100 bytes allocated ==20859== ==20859== All heap blocks were freed -- no leaks are possible ==20859== ==20859== For counts of detected and suppressed errors, rerun with: -v ==20859== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) [sand@PS-CNTOS-64-S11 testbox]$ [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck --leak-check=full ./t1 ==20899== Memcheck, a memory error detector ==20899== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==20899== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info ==20899== Command: ./t1 ==20899== ==20899== Invalid free() / delete / delete[] ==20899== at 0x4A05A31: free (vg_replace_malloc.c:325) ==20899== by 0x4004FF: main (t1.c:8) ==20899== Address 0x4c26040 is 0 bytes inside a block of size 100 free'd ==20899== at 0x4A05A31: free (vg_replace_malloc.c:325) ==20899== by 0x4004F6: main (t1.c:7) ==20899== ==20899== ==20899== HEAP SUMMARY: ==20899== in use at exit: 0 bytes in 0 blocks ==20899== total heap usage: 1 allocs, 2 frees, 100 bytes allocated ==20899== ==20899== All heap blocks were freed -- no leaks are possible ==20899== ==20899== For counts of detected and suppressed errors, rerun with: -v ==20899== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) [sand@PS-CNTOS-64-S11 testbox]$ 

Un correctif possible:

 #include #include int main() { char *x = malloc(100); free(x); x=NULL; free(x); return 0; } [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1 [sand@PS-CNTOS-64-S11 testbox]$ ./t1 [sand@PS-CNTOS-64-S11 testbox]$ [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck --leak-check=full ./t1 ==20958== Memcheck, a memory error detector ==20958== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==20958== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info ==20958== Command: ./t1 ==20958== ==20958== ==20958== HEAP SUMMARY: ==20958== in use at exit: 0 bytes in 0 blocks ==20958== total heap usage: 1 allocs, 1 frees, 100 bytes allocated ==20958== ==20958== All heap blocks were freed -- no leaks are possible ==20958== ==20958== For counts of detected and suppressed errors, rerun with: -v ==20958== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4) [sand@PS-CNTOS-64-S11 testbox]$ 

Consultez le blog sur l’utilisation de Valgrind Link

Utilisez-vous des pointeurs intelligents tels que Boost shared_ptr ? Si c’est le cas, vérifiez si vous utilisez directement le pointeur raw n’importe où en appelant get() . J’ai trouvé que c’était un problème assez courant.

Par exemple, imaginez un scénario dans lequel un pointeur brut est transmis (par exemple, en tant que gestionnaire de rappel) à votre code. Vous pouvez décider d’affecter cela à un pointeur intelligent afin de faire face au comptage de références, etc. Grande erreur: votre code ne possède pas ce pointeur à moins que vous ne preniez une copie complète. Lorsque votre code est fait avec le pointeur intelligent, il le détruira et tentera de détruire la mémoire vers laquelle il pointe car il pense que personne n’en a besoin, mais le code appelant essaiera alors de le supprimer et vous obtiendrez un double. problème gratuit.

Bien sûr, cela pourrait ne pas être votre problème ici. Plus simple, voici un exemple qui montre comment cela peut arriver. La première suppression est correcte, mais le compilateur détecte qu’il a déjà supprimé cette mémoire et pose un problème. C’est pourquoi atsortingbuer 0 à un pointeur immédiatement après la suppression est une bonne idée.

 int main(int argc, char* argv[]) { char* ptr = new char[20]; delete[] ptr; ptr = 0; // Comment me out and watch me crash and burn. delete[] ptr; } 

Edit: a modifié delete pour delete[] , car ptr est un tableau de caractères.

Avec les compilateurs C ++ modernes, vous pouvez utiliser des désinfectants pour effectuer le suivi.

Exemple d’exemple:

Mon programme:

 $cat d_free.cxx #include using namespace std; int main() { int * i = new int(); delete i; //i = NULL; delete i; } 

Comstackr avec des assainisseurs d’adresses:

 # g++-7.1 d_free.cxx -Wall -Werror -fsanitize=address -g 

Exécuter:

 # ./a.out ================================================================= ==4836==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0: #0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 #1 0x400b2c in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:11 #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) #3 0x400a08 (/media/sf_shared/jkr/cpp/d_free/a.out+0x400a08) 0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014) freed by thread T0 here: #0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 #1 0x400b1b in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:9 #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) previously allocated by thread T0 here: #0 0x7f35b2d7a040 in operator new(unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:80 #1 0x400ac9 in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:8 #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) SUMMARY: AddressSanitizer: double-free /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 in operator delete(void*, unsigned long) ==4836==ABORTING 

Pour en savoir plus sur les désinfectants, vous pouvez vérifier ceci ou cela, ou tout autre compilateur c ++ moderne (par exemple, gcc, clang, etc.).

Je sais que c’est un très vieux thread, mais c’est la recherche google supérieure pour cette erreur, et aucune des réponses ne mentionne une cause commune de l’erreur.

Qui ferme un fichier que vous avez déjà fermé.

Si vous ne faites pas attention et que deux fonctions différentes ferment le même fichier, la seconde générera cette erreur.