Hoe een double free of corruptie -fout op te sporen

Als ik mijn (C++) programma start, crasht het met deze fout.

* glibc gedetecteerd *./load: dubbel gratis of beschadigd (!prev):
0x000000000c6ed50 ***

Hoe kan ik de fout opsporen?

Ik heb geprobeerd om print (std::cout) statements te gebruiken, zonder succes. Kan gdbdit gemakkelijker maken?


Antwoord 1, autoriteit 100%

Als u glibc gebruikt, kunt u de omgevingsvariabele MALLOC_CHECK_instellen op 2, hierdoor gebruikt glibc een fouttolerante versie van malloc, wat ervoor zorgt dat je programma wordt afgebroken op het punt waar de double free is gedaan.

U kunt dit vanuit gdb instellen door de opdracht set environment MALLOC_CHECK_ 2te gebruiken voordat u uw programma uitvoert; het programma zou moeten afbreken, met de free()aanroep zichtbaar in de backtrace.

zie de man-pagina voor malloc()voor meer informatie


Antwoord 2, autoriteit 53%

Er zijn minstens twee mogelijke situaties:

  1. je verwijdert dezelfde entiteit twee keer
  2. je verwijdert iets dat niet is toegewezen

Voor de eerste raad ik ten zeerste aan om alle verwijderde aanwijzers op NULL te zetten.

Je hebt drie opties:

  1. overlaad nieuwe en verwijder en volg de toewijzingen
  2. ja, gebruik gdb — dan krijg je een backtrace van je crash, en dat zal waarschijnlijk erg nuttig zijn
  3. zoals voorgesteld — gebruik Valgrind — het is niet gemakkelijk om erin te komen, maar het zal je in de toekomst duizendvoudig tijd besparen…

Antwoord 3, autoriteit 42%

Je kunt gdb gebruiken, maar ik zou eerst Valgrindproberen. Zie de snelstartgids.

Kortom, Valgrind instrumenteert je programma zodat het verschillende soorten fouten kan detecteren bij het gebruik van dynamisch toegewezen geheugen, zoals dubbele frees en schrijft voorbij het einde van toegewezen geheugenblokken (wat de heap kan beschadigen). Het detecteert en rapporteert de fouten zodra ze zich voordoen, waardoor u direct naar de oorzaak van het probleem wordt verwezen.


Antwoord 4, autoriteit 36%

Drie basisregels:

  1. Stel de aanwijzer in op NULLna gratis
  2. Controleer op NULLvoordat u vrijgeeft.
  3. Initialiseer de aanwijzer naar NULLin het begin.

De combinatie van deze drie werkt best goed.


Antwoord 5, autoriteit 26%

Je kunt valgrindgebruiken om het te debuggen.

#include<stdio.h>
#include<stdlib.h>
int main()
{
 char *x = malloc(100);
 free(x);
 free(x);
 return 0;
}
[[email protected] testbox]$ vim t1.c
[[email protected] testbox]$ cc -g t1.c -o t1
[[email protected] 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
[[email protected] testbox]$
[[email protected] testbox]$ vim t1.c
[[email protected] testbox]$ cc -g t1.c -o t1
[[email protected] 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)
[[email protected] testbox]$
[[email protected] 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)
[[email protected] testbox]$

Een mogelijke oplossing:

#include<stdio.h>
#include<stdlib.h>
int main()
{
 char *x = malloc(100);
 free(x);
 x=NULL;
 free(x);
 return 0;
}
[[email protected] testbox]$ vim t1.c
[[email protected] testbox]$ cc -g t1.c -o t1
[[email protected] testbox]$ ./t1
[[email protected] testbox]$

[[email protected] 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)
[[email protected] testbox]$

Bekijk de blog over het gebruik van Valgrind Link


Antwoord 6, autoriteit 23%

Met moderne C++-compilers kun je ontsmettingsmiddelengebruiken om bij te houden.

Voorbeeld voorbeeld:

Mijn programma:

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

Compileren met adresreinigers:

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

Uitvoeren:

# ./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

Voor meer informatie over ontsmettingsmiddelen kun je ditof ditof andere moderne c++-compilers (bijv. gcc, clang etc.) documentatie.


Antwoord 7, autoriteit 8%

Gebruik je slimme tips zoals Boost shared_ptr? Als dit het geval is, controleer dan of u de onbewerkte aanwijzer ergens rechtstreeks gebruikt door get()aan te roepen. Ik heb gemerkt dat dit een veel voorkomend probleem is.

Stel je bijvoorbeeld een scenario voor waarin een onbewerkte aanwijzer wordt doorgegeven (misschien als een callback-handler, bijvoorbeeld) aan je code. Je zou kunnen besluiten om dit toe te wijzen aan een slimme aanwijzer om het tellen van referenties enz. aan te kunnen. Grote fout: je code bezit deze aanwijzer niet, tenzij je een diepe kopie maakt. Wanneer je code klaar is met de slimme aanwijzer, zal deze deze vernietigen en proberen het geheugen waarnaar het verwijst te vernietigen, omdat het denktdat niemand anders het nodig heeft, maarde aanroepende code probeert het dan te verwijderen en je krijgt een dubbel gratis probleem.

Natuurlijk is dat hier misschien niet jouw probleem. Op zijn eenvoudigst is hier een voorbeeld dat laat zien hoe het kan gebeuren. De eerste verwijdering is prima, maar de compiler voelt dat het geheugen al is verwijderd en veroorzaakt een probleem. Daarom is het een goed idee om direct na het verwijderen een 0 toe te wijzen aan een aanwijzer.

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;
}

Bewerken: deletegewijzigd in delete[], aangezien ptr een array van char is.


Antwoord 8

Ik weet dat dit een heel oude thread is, maar het is de belangrijkste Google-zoekopdracht voor deze fout, en geen van de reacties vermeldt een veelvoorkomende oorzaak van de fout.

Hiermee wordt een bestand gesloten dat u al heeft gesloten.

Als je niet oplet en twee verschillende functies hetzelfde bestand sluiten, dan zal de tweede deze fout genereren.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes