Gdy próbuję skompilować kod C korzystający z gets()
funkcji za pomocą GCC, pojawia się następujące ostrzeżenie:
(.text + 0x34): ostrzeżenie: funkcja „gets” jest niebezpieczna i nie należy jej używać.
Pamiętam, że ma to coś wspólnego z ochroną stosu i bezpieczeństwem, ale nie jestem pewien, dlaczego.
Jak mogę usunąć to ostrzeżenie i dlaczego pojawia się takie ostrzeżenie dotyczące używania gets()
?
Jeśli gets()
jest tak niebezpieczny, dlaczego nie możemy go usunąć?
c
fgets
buffer-overflow
gets
vinit dhatrak
źródło
źródło
gets()
Buffer_overflow_attackgets()
Odpowiedzi:
Aby
gets
bezpiecznie korzystać , musisz dokładnie wiedzieć, ile znaków będziesz czytać, abyś mógł odpowiednio zwiększyć swój bufor. Będziesz wiedział tylko, że jeśli dokładnie wiesz, jakie dane będziesz czytać.Zamiast używać
gets
, chcesz użyćfgets
, który ma podpis(
fgets
jeśli odczytuje całą linię, pozostawi'\n'
ciąg znaków; będziesz musiał sobie z tym poradzić.)Pozostał oficjalną częścią języka aż do standardu ISO C z 1999 roku, ale został oficjalnie usunięty przez standard z 2011 roku. Większość implementacji C nadal go obsługuje, ale przynajmniej gcc wydaje ostrzeżenie dla każdego kodu, który go używa.
źródło
gets()
który powoduje, że kompilator emituje ostrzeżenie, gdy jest używane.Dlaczego jest
gets()
niebezpiecznyPierwszy robak internetowy ( Morris Internet Worm ) uciekł około 30 lat temu (1988-11-02) i wykorzystał
gets()
przepełnienie bufora jako jedną ze swoich metod rozprzestrzeniania się z systemu do systemu. Podstawowym problemem jest to, że funkcja nie wie, jak duży jest bufor, więc kontynuuje czytanie, dopóki nie znajdzie nowego wiersza lub nie napotka EOF, i może przekroczyć granice podanego bufora.Powinieneś zapomnieć, że kiedykolwiek słyszałeś o
gets()
istnieniu.Norma C11 ISO / IEC 9899: 2011 została wyeliminowana
gets()
jako funkcja standardowa, którą jest A Good Thing ™ (została formalnie oznaczona jako „przestarzała” i „przestarzała” w ISO / IEC 9899: 1999 / Cor.3: 2007 - Corrigendum techniczne 3 dla C99, a następnie usunięty w C11). Niestety, pozostanie w bibliotekach przez wiele lat (co oznacza „dekady”) ze względu na kompatybilność wsteczną. Gdyby to zależało ode mnie, wdrożeniegets()
byłoby:Biorąc pod uwagę, że Twój kod i tak się zawiesi, prędzej czy później, lepiej jest usunąć problem wcześniej niż później. Byłbym przygotowany na dodanie komunikatu o błędzie:
Nowoczesne wersje systemu kompilacji Linux generują ostrzeżenia, jeśli łączysz
gets()
- a także dla niektórych innych funkcji, które również mają problemy z bezpieczeństwem (mktemp()
,…).Alternatywy dla
gets()
fgets ()
Jak wszyscy inni mówili, kanoniczną alternatywą
gets()
jestfgets()
określeniestdin
jako strumień plików.Nikt jeszcze nie wspomniał, że
gets()
nie zawiera nowej linii, ale jąfgets()
zawiera. Może być konieczne użycie opakowania,fgets()
które usuwa nowy wiersz:Albo lepiej:
Ponadto, jak wskazuje caf w komentarzu, a paxdiablo pokazuje w swojej odpowiedzi, a
fgets()
ty możesz mieć dane w linii. Mój kod opakowania pozostawia te dane do odczytania następnym razem; możesz go łatwo zmodyfikować, aby pochłonąć resztę wiersza danych, jeśli wolisz:Pozostały problem polega na tym, jak zgłosić trzy różne stany wynikowe - EOF lub błąd, odczyt linii i nie obcięty oraz częściowy odczyt linii, ale dane zostały obcięte.
Ten problem nie występuje,
gets()
ponieważ nie wie, gdzie kończy się bufor, i wesoło tratuje za nim, siejąc spustoszenie w pięknie utrzymanym układzie pamięci, często psując stos zwrotny ( przepełnienie stosu ), jeśli bufor jest przydzielony na stos lub deptanie informacji kontrolnych, jeśli bufor jest dynamicznie przydzielany, lub kopiowanie danych przez inne cenne zmienne globalne (lub modułowe), jeśli bufor jest przydzielany statycznie. Żadne z nich nie jest dobrym pomysłem - są one uosobieniem wyrażenia „niezdefiniowane zachowanie”.Istnieje również TR 24731-1 (Raport techniczny komitetu standardowego C), który zapewnia bezpieczniejsze alternatywy dla różnych funkcji, w tym
gets()
:Kompilatory Microsoft Visual Studio implementują zbliżenie do standardu TR 24731-1, ale istnieją różnice między podpisami zaimplementowanymi przez Microsoft a podpisami w TR.
Norma C11, ISO / IEC 9899-2011, zawiera TR24731 w załączniku K jako opcjonalną część biblioteki. Niestety rzadko jest implementowany w systemach uniksopodobnych.
getline()
- POSIXPOSIX 2008 zapewnia również bezpieczną alternatywę dla
gets()
wywoływanychgetline()
. Dynamicznie przydziela miejsce dla linii, więc musisz go zwolnić. Usuwa zatem ograniczenie długości linii. Zwraca również długość odczytanych danych-1
( lub nieEOF
!), Co oznacza, że bajty zerowe na wejściu mogą być obsługiwane niezawodnie. Istnieje również wariant „wybierz własny separator jednoznakowy”getdelim()
; może to być przydatne, jeśli masz do czynienia z danymi wyjściowymi, nafind -print0
których końce nazw plików są oznaczone'\0'
na przykład znakiem NUL ASCII .źródło
fgets()
i twojafgets_wrapper()
wersja pozostawi końcową część zbyt długiej linii w buforze wejściowym, do odczytania przez następną funkcję wejściową. W wielu przypadkach będziesz chciał przeczytać i odrzucić te postacie.getline()
i jego krewnygetdelim()
, które zwracają długość „linii” czytanej przez komendy, przydzielając miejsce zgodnie z wymaganiami, aby móc zapisać całą linię. Nawet to może powodować problemy, jeśli skończy się na jednowierszowym pliku JSON o rozmiarze wielu gigabajtów; możesz sobie pozwolić na całą tę pamięć? (A skoro już to robimy, czy możemy miećstrcpy()
istrcat()
warianty, które zwracają wskaźnik do bajtu zerowego na końcu? Itd.)fgets()
jest to, że jeśli plik zawiera bajt zerowy, nie można powiedzieć, ile danych jest po bajcie zerowym do końca linii (lub EOF).strlen()
może zgłaszać tylko do bajtu zerowego w danych; potem jest to zgadywanie i dlatego prawie na pewno jest błędne.gets()
istnieniu”. Kiedy to robię, znów na nie wpadam i wracam tutaj. Czy hakujesz stackoverflow, aby uzyskać upvotes?Ponieważ
gets
nie wykonuje żadnej kontroli podczas pobierania bajtów ze standardowego wejścia i umieszczania ich gdzieś. Prosty przykład:Teraz przede wszystkim możesz wpisać, ile znaków chcesz,
gets
nie przejmuj się tym. Po drugie, bajty ponad rozmiar tablicy, w której je umieściłeś (w tym przypadkuarray1
) zastąpią wszystko, co znajdą w pamięci, ponieważgets
je zapiszą. W poprzednim przykładzie oznacza to, że jeśli wpiszesz"abcdefghijklmnopqrts"
być może, nieprzewidzianie, nadpisze równieżarray2
lub cokolwiek innego.Ta funkcja jest niebezpieczna, ponieważ zakłada spójne dane wejściowe. NIGDY NIE UŻYWAJ!
źródło
gets
całkowicie bezużyteczny, jest to, że nie ma parametru długości / liczby tablic, który bierze; Gdyby tam był, byłaby to zwykła standardowa funkcja C.gets
i dlaczego nie stworzono żadnego standardowego wariantu fgets jako wygodnego dla przypadków użycia, w których nowa linia nie jest pożądana jako część danych wejściowych?gets
, jak sama nazwa wskazuje, został zaprojektowany, aby uzyskać ciąg znakówstdin
, jednak uzasadnienie braku parametru rozmiaru mogło wynikać z ducha C : Zaufaj programistom. Ta funkcja została usunięta w C11, a podana zamianagets_s
przyjmuje rozmiar bufora wejściowego. Nie mam jednak pojęcia o tejfgets
części.gets
się usprawiedliwić, to sytuacja, w której używa się systemu We / Wy buforowanego linią sprzętową, który fizycznie nie byłby w stanie przesłać linii na pewną długość i zamierzonego czasu życia programu był krótszy niż okres użytkowania sprzętu. W takim przypadku, jeśli sprzęt nie jest w stanie przesłać wierszy o długości przekraczającej 127 bajtów, uzasadnione może być umieszczeniegets
w buforze 128-bajtowym, choć moim zdaniem korzyści wynikające z możliwości określenia krótszego bufora, gdy oczekiwane są mniejsze dane wejściowe, byłyby bardziej niż uzasadnione koszt.gets
istrcat
bezpiecznie zaakceptować tyle, ile będzie pasować.Nie powinieneś używać,
gets
ponieważ nie ma sposobu na powstrzymanie przepełnienia bufora. Jeśli użytkownik wpisze więcej danych, niż może zmieścić się w buforze, najprawdopodobniej skończy się to uszkodzeniem lub gorzej.W rzeczywistości ISO podjęło krok polegający na usunięciu
gets
ze standardu C (od C11, chociaż był przestarzały w C99), co, biorąc pod uwagę, jak wysoko oceniają kompatybilność wsteczną, powinno wskazywać na to, jak zła była ta funkcja.Prawidłową czynnością jest użycie
fgets
funkcji zstdin
uchwytem pliku, ponieważ można ograniczyć liczbę znaków odczytywanych przez użytkownika.Ale ma to również swoje problemy, takie jak:
W tym celu prawie każdy koder C w pewnym momencie swojej kariery napisze bardziej przydatne opakowanie
fgets
. To moje:z pewnym kodem testowym:
Zapewnia takie same zabezpieczenia, jak
fgets
to, że zapobiega przepełnieniu bufora, ale również powiadamia osobę dzwoniącą o tym, co się stało, i usuwa nadmiar znaków, aby nie wpływały na następną operację wprowadzania.Używaj go tak, jak chcesz, niniejszym udostępniam na licencji „rób co chcesz, do cholery” :-)
źródło
gets()
zastąpił jawnie ani w sekcji 7.19.7.7, w której jest zdefiniowany, ani w sekcji 7.26.9 Przyszłe kierunki bibliotek i podrozdział dla<stdio.h>
. Nie ma nawet przypisu, że jest niebezpieczny. (Mimo, że widzę „To przestarzałe w normie ISO / IEC 9899: 1999 / Cor.3: 2007 (E))” w odpowiedzi przez Yu Hao ) Ale C11 nie usunąć go z norma - a nie przed czasem.!int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)
ukrywasize_t
sięint
konwersjisz
.sz > INT_MAX || sz < 2
złapałby dziwne wartościsz
.if (buff[strlen(buff)-1] != '\n') {
jest exploitem hakera, ponieważ pierwszy wprowadzony znak złego użytkownika może być osadzonym znakiembuff[strlen(buff)-1]
UB o wartości zerowej .while (((ch = getchar())...
ma problemy, jeśli użytkownik wprowadzi znak zerowy.fgets .
Aby przeczytać ze standardowego wejścia:
źródło
Nie można usunąć funkcji API bez zerwania interfejsu API. Jeśli tak, wiele aplikacji nie będzie się w ogóle kompilować ani uruchamiać.
To jest powód, dla którego jedno odniesienie daje:
źródło
Niedawno przeczytałem w poście USENET
comp.lang.c
, którygets()
jest usuwany ze standardu. WOOHOOźródło
gcc -std=c2012 -pedantic ...
gets () nie przejdzie. (Właśnie nadrobiłem-std
parametr)W C11 (ISO / IEC 9899: 201x)
gets()
został usunięty. (Jest przestarzałe w ISO / IEC 9899: 1999 / Cor.3: 2007 (E))Oprócz
fgets()
C11 wprowadza nową bezpieczną alternatywęgets_s()
:Jednak w Zalecaną praktyką sekcji
fgets()
jest nadal korzystne.źródło
gets()
jest niebezpieczne, ponieważ użytkownik może zawiesić program, wpisując zbyt wiele w monicie. Nie może wykryć końca dostępnej pamięci, więc jeśli przydzielisz zbyt małą ilość pamięci do tego celu, może to spowodować awarię seg i awarię. Czasami wydaje się bardzo mało prawdopodobne, aby użytkownik wpisał 1000 liter w pytaniu przeznaczonym na nazwisko osoby, ale jako programiści musimy uczynić nasze programy kuloodpornymi. (może to również stanowić zagrożenie bezpieczeństwa, jeśli użytkownik może zawiesić program systemowy, wysyłając zbyt dużo danych).fgets()
pozwala określić, ile znaków jest pobieranych ze standardowego bufora wejściowego, aby nie przekroczyły zmiennej.źródło
Chciałbym poważnie zaprosić wszystkich opiekunów bibliotek C, którzy nadal włączają się
gets
do swoich bibliotek „na wypadek, gdyby ktoś nadal polegał na tym”: Proszę zastąpić swoją implementację odpowiednikiemPomoże to upewnić się, że nikt nie jest zależny od tego. Dziękuję Ci.
źródło
Funkcja C dostaje się jest niebezpieczna i była bardzo kosztownym błędem. Tony Hoare wyróżnia go na szczególną uwagę w swoim przemówieniu „Null References: The Billion Dollar Mistake”:
http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
Warto obejrzeć całą godzinę, ale jego komentarze są wyświetlane od 30 minut, a konkretna krytyka około 39 minut.
Mam nadzieję, że to pobudza apetyt na całą rozmowę, która zwraca uwagę na to, jak potrzebujemy bardziej formalnych dowodów poprawności w językach i jak winić projektantów języków za błędy w ich językach, a nie programistę. Wydaje się, że był to cały wątpliwy powód dla projektantów złych języków, by zrzucić winę na programistów pod postacią „wolności programisty”.
źródło