2013-09-30 26 views
8

Ponieważ istnieje opcja -fstack-protector-strong w gcc do wykrywania rozbijania stosu. Jednak nie zawsze może wykryć przepełnienie bufora stosu. Dla pierwszej funkcji func, kiedy wprowadzę ciąg 10 znaków, program nie zawsze się zawiesza. Moje pytanie dotyczy miejsca, w którym można wykryć przepełnienie bufora stosu.GCC sposób wykrywania przepełnienia bufora stosu

void func() 
{ 
    char array[10]; 
    gets(array); 
} 

void func2() 
{ 
    char buffer[10]; 
    int n = sprintf(buffer, "%s", "abcdefghpapeas"); 
    printf("aaaa [%d], [%s]\n", n, buffer); 
} 

int main() 
{ 
    func(); 
    func2(); 
} 
+4

Dlaczego tagujesz pytanie C++, a następnie piszesz kod w ten sposób? –

+0

Czy nie byłoby bardziej logiczne unikanie tego na pierwszym miejscu? Na przykład, używając 'snprintf (bufor, sizeof (bufor),"% s ", ...)' – mvp

+2

Jest tam, aby wykryć, czy stos jest rozbity, aby nie wykryć przepełnienia bufora. Użyj więcej znaków. –

Odpowiedz

6

przepełnienia stosu są albo trudne do wykrycia lub bardzo drogie do wykrycia - wybrał swoją truciznę.

W skrócie, jeśli masz ten:

char a,b; 
char *ptr=&a; 
ptr[1] = 0; 

to jest technicznie prawna: Jest przestrzeń przeznaczono na stosie, która należy do funkcji. To bardzo niebezpieczne.

Rozwiązaniem może być dodanie luki między a a b i wypełnienie jej deseniem. Ale, cóż, niektórzy ludzie faktycznie piszą kod jak wyżej. Twój kompilator musi to wykryć.

Alternatywnie, możemy stworzyć mapę bitową wszystkich bajtów, które twój kod naprawdę przydzielił, a następnie przydzielić cały kod do sprawdzenia na tej mapie. Bardzo bezpieczny, dość powolny, zwiększa twoje wykorzystanie pamięci. Po stronie pozytywnej znajdują się narzędzia, które mogą w tym pomóc (np. Valgrind).

Zobacz, dokąd zmierzam?

Wniosek: W języku C nie ma dobrego sposobu na automatyczne wykrywanie wielu problemów z pamięcią, ponieważ język i interfejs API są często zbyt niepomyślne. Rozwiązaniem jest przeniesienie kodu do funkcji pomocniczych, które rygorystycznie sprawdzają swoje parametry, zawsze w dobrym kierunku i mają dobry zasięg testów jednostkowych.

Zawsze używaj wersji snprintf(), jeśli masz wybór. Jeśli stary kod używa niebezpiecznych wersji, zmień go.

+0

+1 dla "Jeśli stary kod używa niebezpiecznych wersji, zmień go." – Degustaf

2

Można użyć narzędzia o nazwie Valgrind

http://valgrind.org/

+0

To jest nieprawidłowe. Valgrind to ładne narzędzie, ale ** nie ** wykrywa przepełnień bufora w stosach. Zobacz tutaj: http://stackoverflow.com/a/29842977/4467665. – thatWiseGuy

+0

AddressSanitizer byłby lepszym wyborem, jest o wiele bardziej precyzyjny, np. potrafi przepełnić stos. – yugr

+0

Valgrind może wykrywać przepełnienia stosu, np. 'Przepełnienie stosu w wątku # 1: nie może rosnąć stos ...' może potrzebujesz informacji debugowania. –

1

Moje pytanie jest tam, gdzie jest to sposób na wykrycie przepełnienie bufora stosu ...

void func() 
{ 
    char array[10]; 
    gets(array); 
} 

void func2() 
{ 
    char buffer[10]; 
    int n = sprintf(buffer, "%s", "abcdefghpapeas"); 
    printf("aaaa [%d], [%s]\n", n, buffer); 
} 

Ponieważ używasz GCC, można użyć FORTIFY_SOURCES.

FORTIFY_SOURCE używa "bezpieczniejszych" wariantów funkcji wysokiego ryzyka, takich jak memcpy, strcpy i gets. Kompilator używa bezpieczniejszych wariantów, gdy może wydedukować rozmiar bufora docelowego. Jeśli kopia przekroczyłaby docelowy rozmiar bufora, program wywoła abort(). Jeśli kompilator nie może wydedukować docelowego rozmiaru bufora, wówczas warianty "bezpieczniejsze" nie są używane.

Aby wyłączyć test FORTIFY_SOURCE, należy skompilować program pod numerem -U_FORTIFY_SOURCE lub -D_FORTIFY_SOURCE=0.


C Standardowy ma "bezpiecznych" funkcji przez ISO/IEC TR 24731-1, Bounds Checking Interfaces. Na zgodnych platformach można po prostu zadzwonić pod numer gets_s i sprintf_s.Oferują one spójne zachowanie (jak zawsze zapewnienie, że ciąg jest kończony NULL) i spójne wartości zwracane (jak 0 w przypadku sukcesu lub errno_t).

Niestety, gcc i glibc nie są zgodne ze standardem C. Ulrich Drepper (jeden z opiekunów glibc) nazwał interfejsy sprawdzania granic "horribly inefficient BSD crap" i nigdy nie zostały one dodane. Mam nadzieję, że to się zmieni w przyszłości.

0

Przede wszystkim nie korzystaj z get.By teraz prawie wszyscy znają wszystkie problemy związane z bezpieczeństwem i niezawodnością, które mogą wystąpić podczas pobierania. Ale uwzględniono to również z przyczyn historycznych, ponieważ jest to bardzo dobry przykład złego programowania. wygląd

Let na wszystkich problemów z kodem:

// Really bad code 
char line[100]; 
gets(line); 

Bo dostaje nie robić granice sprawdzanie ciąg dłuższy niż 100 znaków zastąpi pamięć. Jeśli masz szczęście, program się po prostu zawiesza. Może to dziwne zachowanie.

Funkcja uzyskiwania jest tak zła, że ​​łącznik GNU gcc wydaje ostrzeżenie, gdy jest używany.

/tmp/ccI5WJ5m.o(.text+0x24): In function `main': 
: warning: the `gets' function is dangerous and should not be used. 

Protect macierz dostępów z assert

C/C++ nie robi sprawdzanie zakresu.

na przykład:

int data[10] 

i = 20 
data[20] = 100 //Memory Corruption 

funkcja Zastosowanie Assert dla powyższego kodu

#include<assert.h> 


int data[10]; 
i=20 

assert((i >= 0) && (i < sizeof(data)/sizeof(data[0]))); // throws 

data[i] = 100 

przepełnienia Array są jednym z najczęstszych błędów programistycznych i są bardzo frustrujące, aby spróbować zlokalizować. Ten kod nie eliminuje ich, ale powoduje, że błędny kod jest przerywany wcześniej w sposób, który znacznie ułatwia znalezienie problemu.

Użyj Snprintf (bufor, sizeof (bufor), "% s", "abcdefghpapeas") i niektóre narzędzia, takie jak valgrind, GDB.

Mam nadzieję, że to pomoże ..

Powiązane problemy