"Çifte serbestlik veya bozulma" hatası nasıl tespit edilir


92

(C ++) programımı çalıştırdığımda bu hata ile çöküyor.

* glibc algılandı * ./load: çift serbestlik veya bozulma (! prev): 0x0000000000c6ed50 ***

Hatayı nasıl bulabilirim?

std::coutBaşarı olmadan print ( ) ifadelerini kullanmayı denedim . Bunu gdbkolaylaştırabilir mi?


5
Neden herkesin NULLişaretçiler için öneride bulunduğunu merak ediyorum (bu sorunun güzel bir şekilde gösterdiği gibi, aksi halde yakalanan hataları maskeliyor), ama hiç kimse manuel bellek yönetimini hiç yapmamayı önermiyor, ki bu C ++ 'da çok iyi. deleteYıllardır yazmadım . (Ve evet, kodum performans açısından kritiktir. Aksi takdirde C ++ ile
yazılmazdı

2
@sbi: Yığın bozulması ve benzerleri nadiren yakalanır, en azından meydana geldikleri yerde değil. NULLing işaretçileri programınızın daha erken çökmesine neden olabilir.
Hasturkun

@Hasturkun: Kesinlikle katılmıyorum. İşaretçiler için önemli bir teşvik, NULLbir saniyenin delete ptr;patlamasını önlemektir - ki bu bir hatayı maskelemektedir, çünkü o saniye deleteasla olmamalıydı. (Ayrıca bir işaretçinin geçerli bir nesneyi işaret edip etmediğini kontrol etmek için de kullanılır. Ancak bu, neden kapsamda gösterecek bir nesnesi olmayan bir işaretçiye sahip olduğunuz sorusunu gündeme getirir.)
sbi

Yanıtlar:


64

Glibc kullanıyorsanız, MALLOC_CHECK_ortam değişkenini olarak ayarlayabilirsiniz 2, bu glibc'nin hataya dayanıklı bir sürümünü kullanmasına mallocneden olur ve bu da programınızın çift serbest bırakma işleminin yapıldığı noktada iptal edilmesine neden olur.

Bunu set environment MALLOC_CHECK_ 2programınızı çalıştırmadan önce komutu kullanarak gdb'den ayarlayabilirsiniz ; free()geri izlemede görünen çağrı ile program iptal edilmelidir .

daha fazla bilgi için man sayfasınamalloc() bakın


2
Ayar MALLOC_CHECK_2aslında çift serbest sorunumu çözdü (yalnızca hata ayıklama modunda ise düzeltilmese de)
puk

4
@puk Bende de aynı sorun var, MALLOC_CHECK_'i 2'ye ayarlamak çifte serbest sorunumdan kaçınıyor. Sorunu yeniden üretmek ve bir geri izleme sağlamak için kod kadar daha az enjekte etmek için başka hangi seçenekler var?
Wei Zhong

Ayrıca, MALLOC_CHECK_ ayarının sorunu ortadan kaldırdığı yerde de bulundurun. Dost yorumcular / herhangi biri ... sorunu sergilemenin farklı bir yolunu buldunuz mu?
pellucidcoder

"MALLOC_CHECK_, sıfır olmayan bir değere ayarlandığında, aynı bağımsız değişkenle çift serbest çağrılar veya tek bir bayt aşımı (kapalı -bir böcekler). " gnu.org/software/libc/manual/html_node/… Görünüşe göre MALLOC_CHECK_ sadece basit bellek hatalarını önlemek için kullanılıyor, bunları tespit etmek için değil.
pellucidcoder

Aslında .... support.microfocus.com/kb/doc.php?id=3113982, MALLOC_CHECK_'i 3 olarak ayarlamak en kullanışlı yöntem gibi görünüyor ve hataları tespit etmek için kullanılabilir.
pellucidcoder

32

En az iki olası durum vardır:

  1. aynı varlığı iki kez siliyorsun
  2. tahsis edilmemiş bir şeyi siliyorsun

İlki için, silinen tüm işaretçileri NULL -ing'i şiddetle tavsiye ediyorum.

Üç seçeneğiniz var:

  1. yeni aşırı yükleme yapın ve tahsisleri silin ve izleyin
  2. evet, gdb kullanın - o zaman çökmenizden geriye dönük bir iz elde edersiniz ve bu muhtemelen çok yardımcı olur
  3. önerildiği gibi - Valgrind'i kullanın - içine girmek kolay değildir, ancak gelecekte size bin kat zaman kazandıracaktır ...

2. yolsuzluğa neden olabilir, ancak bu mesajın genellikle görüneceğini sanmıyorum, çünkü akıl sağlığı kontrolü yalnızca yığın üzerinde yapılır. Ancak, bence 3. yığın arabellek taşması mümkün.
Matthew Flaschen

İyi bir. Doğru, işaretçiyi NULL yapmayı özledim ve bu hatayla karşılaştım. Öğrenilen dersler!
hrushi

26

Gdb'yi kullanabilirsiniz, ancak önce Valgrind'i deneyeceğim . Hızlı başlangıç ​​kılavuzuna bakın .

Kısaca, Valgrind, programınızı, dinamik olarak ayrılmış bellek kullanımında, ayrılmış bellek bloklarının sonunu geçme (bu da yığını bozabilir) gibi çift serbest bırakma ve yazma gibi çeşitli hataları algılayabilmesi için kullanır. Hataları oluşur oluşmaz algılar ve rapor eder , böylece sizi doğrudan sorunun nedenine yönlendirir.


1
@SMR, bu durumda cevabın temel parçaları, büyük, bağlantılı sayfanın tamamıdır. Yani yanıta sadece bağlantı eklemek tamamen iyidir. Yazarın neden Valgrind'i gdb'ye tercih ettiğine ve belirli bir sorunu nasıl çözeceğine dair birkaç söz , cevapta gerçekten eksik olan IMHO'dur.
ndemou

20

Üç temel kural:

  1. İşaretçiyi NULLserbest bıraktıktan sonra olarak ayarla
  2. NULLSerbest bırakmadan önce kontrol edin .
  3. Başlangıçta işaretçiyi NULLbaşlat.

Bu üçünün kombinasyonu oldukça iyi çalışıyor.


1
C uzmanı değilim, ama genellikle kafamı suyun üzerinde tutabilirim. Neden # 1? Sadece sessiz bir hata değil, serbest bırakılmış bir işaretçiye erişmeye çalıştığınızda programınız tamamen çöküyor mu?
Daniel Harms

1
@Precision: Evet, mesele bu. Bu iyi bir uygulamadır: silinen belleği gösteren bir işaretçiye sahip olmak bir risktir.
ereOn

10
Çoğu derleyici, bir soruna neden olmadan boş bir işaretçiyi silmenize izin vereceğinden, kesinlikle # 2'nin gereksiz olduğunu düşünüyorum. Yanılıyorsam birisi beni düzelteceğinden eminim. :)
Bileşen 10

11
@ Component10 C standardı tarafından hiçbir şey yapmamak için NULL'u serbest bırakmanın gerekli olduğunu düşünüyorum.
Demi

2
@Demetri: Evet, haklısınız "silme işleminin değeri boş göstericiyse işlemin hiçbir etkisi olmaz." (ISO / IEC 14882: 2003 (E) 5.3.5.2)
Bileşen 10

15

valgrindHata ayıklamak için kullanabilirsiniz .

#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]$

Olası bir çözüm:

#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]$

Valgrind Bağlantısını kullanma hakkındaki bloga göz atın


Programımın çalışması yaklaşık 30 dakika sürüyor, Valgrind'de bitirmek 18 ila 20 saat sürebilir.
Kemin Zhou

11

Modern C ++ derleyicileriyle , izlemek için dezenfektanları kullanabilirsiniz .

Örnek örnek:

Programım:

$cat d_free.cxx 
#include<iostream>

using namespace std;

int main()

{
   int * i = new int();
   delete i;
   //i = NULL;
   delete i;
}

Adres temizleyicileri ile derleyin:

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

Yürüt:

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

Dezenfektanlar hakkında daha fazla bilgi edinmek için, bunu veya bu veya herhangi bir modern c ++ derleyicisini (örneğin gcc, clang vb.) Kontrol edebilirsiniz .


5

Boost gibi akıllı işaretçiler kullanıyor musunuz shared_ptr? Eğer öyleyse, ham işaretçiyi herhangi bir yerde doğrudan arayarak kullanıp kullanmadığınızı kontrol edin.get() . Bunu oldukça yaygın bir sorun olarak buldum.

Örneğin, kodunuza ham bir işaretçinin (örneğin bir geri arama işleyicisi olarak) iletildiği bir senaryo hayal edin. Referans sayma vb. İle başa çıkmak için bunu akıllı bir işaretçiye atamaya karar verebilirsiniz. Büyük hata: Derin bir kopya almadığınız sürece kodunuz bu işaretçiye sahip değildir. Kodunuz akıllı işaretçi ile bittiğinde, onu yok edecek ve başka kimsenin buna ihtiyacı olmadığını düşündüğü için işaret ettiği belleği yok etmeye çalışacaktır , ancak çağıran kod daha sonra onu silmeye çalışacak ve bir çift alacaksınız. ücretsiz sorun.

Tabii ki, buradaki probleminiz bu olmayabilir. En basit haliyle işte bunun nasıl olabileceğini gösteren bir örnek. İlk silme işlemi tamamdır, ancak derleyici bu belleği zaten sildiğini ve bir soruna neden olduğunu algılar. Bu nedenle, silme işleminden hemen sonra bir işaretçiye 0 atamak iyi bir fikirdir.

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

Düzenleme: değiştirildi deleteiçin delete[]ptr char bir dizi olduğu için.


Programımda herhangi bir silme komutu kullanmadım. Hala sorun bu olabilir mi?
neuromancer

1
@Phenom: Neden silme işlemlerini kullanmadınız? Akıllı işaretçiler kullandığınız için mi? Muhtemelen yığın üzerinde nesneler oluşturmak için kodunuzda yeniyi kullanıyorsunuz? Bunların her ikisinin de cevabı evet ise, ham işaretçileri kopyalamak için akıllı işaretçilerde get / set kullanıyor musunuz? Eğer öyleyse, yapma! Referans sayımını bozarsın. Alternatif olarak, aradığınız kitaplık kodundan bir akıllı işaretçiye bir işaretçi atayabilirsiniz. Belirtilen belleğe 'sahip değilseniz', bunu yapmayın, çünkü hem kitaplık hem de akıllı işaretçi onu silmeye çalışacaktır.
Bileşen 10

-2

Bunun çok eski bir konu olduğunu biliyorum, ancak bu hataya yönelik en iyi google araması ve yanıtların hiçbiri hatanın ortak bir nedeninden bahsetmiyor.

Bu, zaten kapatmış olduğunuz bir dosyayı kapatmaktır.

Dikkat etmiyorsanız ve aynı dosyayı kapatan iki farklı işlev varsa, ikincisi bu hatayı oluşturacaktır.


Yanılıyorsunuz, bu hata, tam olarak hata durumlarında olduğu gibi, çifte serbestlik nedeniyle atılır. Bir dosyayı iki kez kapattığınız gerçeği, iki kat serbestliğe neden oluyor, çünkü kapatma yöntemi aynı verileri iki kez serbest bırakmaya çalışıyor.
Geoffrey
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.