2015-11-24 17 views
8

Wiem, że istnieje już wiele podobnych pytań i odpowiedzi, ale nie jestem w stanie rozwiązać mojego problemu.Dowiedz się, gdzie pamięć sterty zostanie uszkodzona.

W mojej dużej aplikacji sterty są coraz uszkodzony i nie jestem w stanie zlokalizować go. Użyłem także narzędzia, takiego jak gflagi, ale bez powodzenia.

Próbowałem GFlags na poniższym przykładzie, który psuje sterty według przeznaczenia:

char* pBuffer = new char[256]; 
memset(pBuffer, 0, 256 + 1); 
delete[] pBuffer; 

Na linii # 2 sterty jest nadpisane, ale jak znaleźć go za pomocą narzędzi takich jak GFlags, windbg itd. Może nie jestem używając poprawnie gflagów.

+3

Dlaczego '256 + 1' w' memset', kiedy przydzielono tylko '256' bajtów? –

+3

W swojej większej aplikacji, skąd wiesz, że sterty zostały uszkodzone? Jakie narzędzie cię o tym poinformowało? – PaulMcKenzie

+3

@ T.Z. Aby zademonstrować rodzaj korupcji, która mogłaby wystąpić ... – StoryTeller

Odpowiedz

1

Jeśli ta sama zmienna jest konsekwentnie uszkodzona, punkty przerwania danych są szybkim i prostym sposobem na znalezienie kodu odpowiedzialnego za zmianę (jeśli IDE je obsługuje). (Debuguj-> Nowy punkt przerwania-> Nowy punkt przerwania danych ... w MS Visual Studio 2008). Nie pomogą, jeśli zepsucie sterty będzie bardziej przypadkowe (ale pomyślałem, że podzielę się prostą odpowiedzią na wypadek, gdyby to pomogło).

0

Istnieje narzędzie o nazwie ogrodzenie elektryczne, które moim zdaniem jest obsługiwane również w systemie Windows.

Zasadniczo chodzi o to, aby porwać malloc i spółkę, aby każdy koniec alokacji znajdował się na granicy strony i oznaczać następną stronę jako niedostępną.

Efekt polega na tym, że przy przepełnieniu bufora występuje błąd seg.

Prawdopodobnie istnieje również opcja opróżnienia bufora.

0

Proszę przeczytać ten link Visual Studio - how to find source of heap corruption errors

Is there a good Valgrind substitute for Windows?

Mówi technikę do znajdowania problemów sterty na oknach.

Ale z drugiej strony zawsze możesz napisać (jeśli piszesz nowy kod) menedżerów pamięci. Sposób wykonania jest następujący: użyj opakowania apis, które będzie wywoływać malloc/calloc itp.

Załóżmy, że masz api myMalloc (size_t len); następnie wewnątrz funkcji możesz spróbować przydzielić HEADER + len + FOOTER. W nagłówku zapisz informacje takie jak rozmiar alokacji lub może być więcej informacji. Na stopce dodaj kilka magicznych liczb, takich jak deadbeef. I zwróć ptr (z malloc) + HEADER z myMalloc.

Uwolnienie go za pomocą myfree (void * ptr), po prostu wykonaj ptr -HEADER, sprawdź len, a następnie skocz na FOOTER = ptr-HEADER + naprawdę allcated len. Przy tej korekcie powinieneś znaleźć deadbeef, a jeśli nie znajdziesz, to wiesz, że jest zepsuty.

0

Jeśli narzędzia zautomatyzowane (takie jak ogrodzenie elektryczne lub valgrind) nie rozwiązują problemu, i uważnie wpatrując się w kod, aby spróbować określić, gdzie mogło dojść do zła, nie pomaga i wyłącza/umożliwia różne operacje (dopóki nie uzyskasz korelacji między obecnością zepsucia sterty a operacjami, które wykonały lub nie wykonałeś wcześniej) aby zawęzić to nie działa, zawsze możesz spróbować tej techniki, która próbuje znaleźć korupcję wcześniej niż później tak, aby łatwiej wyśledzić źródło:

tworzyć własne nowe i usunąć operatorów, które wystawią obszary ochronne korupcyjnych oczywiste wokół przydzielonych obszarów pamięci, coś takiego:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <new> 

// make this however big you feel is "big enough" so that corrupted bytes will be seen in the guard bands 
static int GUARD_BAND_SIZE_BYTES = 64; 

static void * MyCustomAlloc(size_t userNumBytes) 
{ 
    // We'll allocate space for a guard-band, then space to store the user's allocation-size-value, 
    // then space for the user's actual data bytes, then finally space for a second guard-band at the end. 
    char * buf = (char *) malloc(GUARD_BAND_SIZE_BYTES+sizeof(userNumBytes)+userNumBytes+GUARD_BAND_SIZE_BYTES); 
    if (buf) 
    { 
     char * w = buf; 
     memset(w, 'B', GUARD_BAND_SIZE_BYTES);   w += GUARD_BAND_SIZE_BYTES; 
     memcpy(w, &userNumBytes, sizeof(userNumBytes)); w += sizeof(userNumBytes); 
     char * userRetVal = w;       w += userNumBytes; 
     memset(w, 'E', GUARD_BAND_SIZE_BYTES);   w += GUARD_BAND_SIZE_BYTES; 
     return userRetVal; 
    } 
    else throw std::bad_alloc(); 
} 

static void MyCustomDelete(void * p) 
{ 
    if (p == NULL) return; // since delete NULL is a safe no-op 

    // Convert the user's pointer back to a pointer to the top of our header bytes 
    char * internalCP = ((char *) p)-(GUARD_BAND_SIZE_BYTES+sizeof(size_t)); 

    char * cp = internalCP; 
    for (int i=0; i<GUARD_BAND_SIZE_BYTES; i++) 
    { 
     if (*cp++ != 'B') 
     { 
      printf("CORRUPTION DETECTED at BEGIN GUARD BAND POSITION %i of allocation %p\n", i, p); 
      abort(); 
     } 
    } 

    // At this point, (cp) should be pointing to the stored (userNumBytes) field 
    size_t userNumBytes = *((const size_t *)cp); 
    cp += sizeof(userNumBytes); // skip past the user's data 
    cp += userNumBytes; 

    // At this point, (cp) should be pointing to the second guard band 
    for (int i=0; i<GUARD_BAND_SIZE_BYTES; i++) 
    { 
     if (*cp++ != 'E') 
     { 
      printf("CORRUPTION DETECTED at END GUARD BAND POSITION %i of allocation %p\n", i, p); 
      abort(); 
     } 
    } 

    // If we got here, no corruption was detected, so free the memory and carry on 
    free(internalCP); 
} 

// override the global C++ new/delete operators to call our 
// instrumented functions rather than their normal behavior 
void * operator new(size_t s) throw(std::bad_alloc) {return MyCustomAlloc(s);} 
void * operator new[](size_t s) throw(std::bad_alloc) {return MyCustomAlloc(s);} 
void operator delete(void * p) throw()     {MyCustomDelete(p);} 
void operator delete[](void * p) throw()     {MyCustomDelete(p);} 

... powyższe elementy wystarczą, aby uzyskać funkcjonalność w stylu ogrodzenia elektrycznego, pod warunkiem, że cokolwiek napisze na jednym z dwóch 64-bajtowych "pasm ochronnych" na początku lub na końcu każdej nowej/usuwanej pamięci -alokacja, a następnie po usunięciu alokacji, MyCustomDelete() zauważy uszkodzenie i awarię programu.

Jeśli to nie jest wystarczająco dobre (np. Ponieważ do czasu usunięcia pojawiło się tak wiele od czasu korupcji, że trudno powiedzieć, co spowodowało uszkodzenie), możesz pójść jeszcze dalej, dodając przydzielone MyCustomAlloc() bufor do pojedynczej/globalnej podwójnie połączonej listy przydziałów i niech MyCustomDelete() usunie ją z tej samej listy (upewnij się, że serializuje te operacje, jeśli twój program jest wielowątkowy!). Zaletą takiego działania jest możliwość dodania innej funkcji zwanej np. CheckForHeapCorruption(), które będzie iterować na tej połączonej liście i sprawdzać zakresy zabezpieczeń dla każdej alokacji na połączonej liście i raportować, czy którykolwiek z nich został uszkodzony. Następnie możesz spryskiwać wywołania funkcji CheckForHeapCorruption() w całym kodzie, tak aby po wystąpieniu uszkodzenia sterty zostanie wykryte podczas następnego wywołania funkcji CheckForHeapCorruption(), a nie później. W końcu okaże się, że jedno wywołanie CheckForHeapCorruption() przeszło z kolorami latającymi, a następnie następne wywołanie CheckForHeapCorruption(), zaledwie kilka linii później, wykryło uszkodzenie, w którym to momencie wiesz, że uszkodzenie było spowodowane przez kod wykonany między dwa wywołania CheckForHeapCorruption(), a następnie możesz przestudiować ten kod, aby dowiedzieć się, co robi źle, i/lub dodać więcej wywołań CheckForHeapCorruption() do tego kodu, jeśli to konieczne.

Powtarzaj, aż błąd stanie się oczywisty. Powodzenia!

Powiązane problemy