Quando executo meu programa (C ++), ele trava com este erro.
* glibc detectado * ./load: double free ou corrompido (! prev): 0x0000000000c6ed50 ***
Como posso rastrear o erro?
Tentei usar std::cout
instruções print ( ), sem sucesso. Poderia gdb
tornar isso mais fácil?
NULL
ponteiros (que mascaram erros que de outra forma seriam detectados, como essa pergunta mostra), mas ninguém sugere simplesmente não fazer gerenciamento manual de memória, o que é muito bem possível em C ++. Eu não escrevo hádelete
anos. (E, sim, meu código é crítico para o desempenho. Caso contrário, não teria sido escrito em C ++.)NULL
ponteiros podem fazer seu programa travar mais cedo.NULL
ponteiros é evitar que um segundodelete ptr;
exploda - o que está mascarando um erro, porque aquele segundodelete
nunca deveria ter acontecido. (Também é usado para verificar se um ponteiro ainda está apontando para um objeto válido. Mas isso só levanta a questão de por que você tem um ponteiro no escopo que não tem um objeto para apontar.)Respostas:
Se você estiver usando glibc, você pode definir a
MALLOC_CHECK_
variável de ambiente para2
, isso fará com que a glibc use uma versão tolerante a erros domalloc
, o que fará com que seu programa aborte no ponto onde o double free é feito.Você pode definir isso a partir do gdb usando o
set environment MALLOC_CHECK_ 2
comando antes de executar o programa; o programa deve abortar, com afree()
chamada visível no backtrace.veja a página
malloc()
do manual para mais informaçõesfonte
MALLOC_CHECK_2
na verdade, corrigiu meu problema gratuito duplo (embora não esteja corrigindo se estiver no modo de depuração apenas)Existem pelo menos duas situações possíveis:
Para o primeiro, eu sugiro enfaticamente anular todos os ponteiros excluídos.
Você tem três opções:
fonte
Você pode usar o gdb, mas primeiro tentaria o Valgrind . Consulte o guia de início rápido .
Resumidamente, Valgrind instrumenta seu programa para que possa detectar vários tipos de erros no uso de memória alocada dinamicamente, como liberações duplas e gravações após o final dos blocos alocados de memória (que podem corromper o heap). Ele detecta e relata os erros assim que ocorrem , apontando assim diretamente para a causa do problema.
fonte
Três regras básicas:
NULL
depois de livreNULL
antes de liberar.NULL
no início.A combinação desses três funciona muito bem.
fonte
Você pode usar
valgrind
para depurá-lo.#include<stdio.h> #include<stdlib.h> 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]$
Uma solução possível:
#include<stdio.h> #include<stdlib.h> 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]$
Confira o blog sobre como usar o Valgrind Link
fonte
Com compiladores C ++ modernos, você pode usar desinfetantes para rastrear.
Exemplo de amostra:
Meu programa:
$cat d_free.cxx #include<iostream> using namespace std; int main() { int * i = new int(); delete i; //i = NULL; delete i; }
Compile com desinfetantes de endereço:
# g++-7.1 d_free.cxx -Wall -Werror -fsanitize=address -g
Execute:
# ./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
Para aprender mais sobre desinfetantes, você pode verificar esta ou esta ou qualquer outra documentação de compiladores c ++ modernos (por exemplo, gcc, clang etc.).
fonte
Você está usando ponteiros inteligentes, como Boost
shared_ptr
? Em caso afirmativo, verifique se você está usando diretamente o ponteiro bruto em qualquer lugar chamandoget()
. Descobri que este é um problema bastante comum.Por exemplo, imagine um cenário onde um ponteiro bruto é passado (talvez como um manipulador de retorno de chamada, digamos) para seu código. Você pode decidir atribuí-lo a um ponteiro inteligente para lidar com a contagem de referência, etc. Grande erro: seu código não possui este ponteiro a menos que você faça uma cópia profunda. Quando seu código estiver pronto com o ponteiro inteligente, ele irá destruí-lo e tentar destruir a memória para a qual aponta, uma vez que pensa que ninguém mais precisa dele, mas o código de chamada tentará excluí-lo e você obterá um duplo problema grátis.
Claro, esse pode não ser o seu problema aqui. Na sua forma mais simples, aqui está um exemplo que mostra como isso pode acontecer. A primeira exclusão está bem, mas o compilador percebe que essa memória já foi excluída e causa um problema. É por isso que atribuir 0 a um ponteiro imediatamente após a exclusão é uma boa ideia.
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: alterado
delete
paradelete[]
, pois ptr é um array de char.fonte
Eu sei que este é um tópico muito antigo, mas é a principal pesquisa do Google para esse erro, e nenhuma das respostas menciona uma causa comum do erro.
Que está fechando um arquivo que você já fechou.
Se você não estiver prestando atenção e tiver duas funções diferentes fechando o mesmo arquivo, a segunda irá gerar este erro.
fonte