Po pierwsze, strlcpy
nigdy nie był pomyślany jako bezpieczna wersja strncpy
(i strncpy
nigdy nie był pomyślany jako bezpieczna wersja strcpy
). Te dwie funkcje są zupełnie niezwiązane. strncpy
jest funkcją, która w ogóle nie ma związku z napisami C (tj. łańcuchami zakończonymi znakiem null). Fakt, że ma str...
przedrostek w nazwie, jest tylko historyczną pomyłką. Historia i cel programu strncpy
są dobrze znane i dobrze udokumentowane. Jest to funkcja stworzona do pracy z tak zwanymi łańcuchami o stałej szerokości (nie z łańcuchami C) używanymi w niektórych historycznych wersjach systemu plików Unix. Niektórzy programiści są dziś zdezorientowani przez jego nazwę i zakładają, że strncpy
ma ona w jakiś sposób służyć jako funkcja kopiowania łańcuchów C o ograniczonej długości („bezpieczne” rodzeństwostrcpy
), co w rzeczywistości jest kompletnym nonsensem i prowadzi do złej praktyki programistycznej. Biblioteka standardowa C w swojej obecnej formie nie ma żadnej funkcji do kopiowania łańcuchów C o ograniczonej długości. To jest miejsce, w którym strlcpy
pasuje. strlcpy
To rzeczywiście prawdziwa funkcja kopiowania o ograniczonej długości, stworzona do pracy z napisami C. strlcpy
poprawnie robi wszystko, co powinna robić funkcja kopiowania o ograniczonej długości. Jedyną krytyką, jaką można do niego skierować, jest to, że niestety nie jest to standard.
Po drugie, strncat
z drugiej strony, jest to rzeczywiście funkcja, która działa ze stringami C i wykonuje konkatenację o ograniczonej długości (jest rzeczywiście „bezpiecznym” rodzeństwem strcat
). Aby prawidłowo korzystać z tej funkcji programista musi zachować szczególną ostrożność, ponieważ parametr rozmiaru, który ta funkcja akceptuje, nie jest tak naprawdę wielkością bufora, który otrzymuje wynik, ale raczej rozmiarem jego pozostałej części (także jest liczony niejawnie). Może to być mylące, ponieważ aby powiązać ten rozmiar z rozmiarem bufora, programista musi pamiętać o wykonaniu dodatkowych obliczeń, które są często używane do krytykowania pliku strncat
.strlcat
załatwia te kwestie, zmieniając interfejs tak, aby nie były potrzebne żadne dodatkowe obliczenia (przynajmniej w kodzie wywołującym). Ponownie, jedyną podstawą, na której można to krytykować, jest to, że funkcja nie jest standardowa. Ponadto funkcje z strcat
grupy są czymś, czego nie zobaczysz w profesjonalnym kodzie bardzo często ze względu na ograniczoną użyteczność samej idei konkatenacji ciągów opartej na ponownym skanowaniu.
Jeśli chodzi o to, jak te funkcje mogą prowadzić do problemów z bezpieczeństwem ... Po prostu nie mogą. Nie mogą prowadzić do problemów bezpieczeństwa w większym stopniu niż sam język C może „prowadzić do problemów bezpieczeństwa”. Widzisz, od dłuższego czasu panowało przekonanie, że język C ++ musi podążać w kierunku rozwijania się w dziwny smak Javy. Ten sentyment czasami przenika również do domeny języka C, co skutkuje raczej nieświadomą i wymuszoną krytyką cech języka C i funkcji standardowej biblioteki C. Podejrzewam, że w tym przypadku możemy mieć do czynienia z czymś takim, chociaż mam nadzieję, że tak naprawdę nie jest tak źle.
strlcpy
istrlcat
zgłosił jakiś rodzaj błędu, gdyby zderzył się z limitem rozmiaru bufora docelowego. Chociaż możesz sprawdzić zwracaną długość, aby to sprawdzić, nie jest to oczywiste. Ale myślę, że to drobna krytyka. Argument „zachęcają do używania ciągów C, więc są źli” jest głupi.strcpy
jest powyżej progu, a zatem są „niepewne”, a ich preferowana funkcja kopiowania ciągów znaków (czy jeststrlcpy
,strcpy_s
czy nawetstrncpy
) jest poniżej progu, a zatem jest „bezpieczna”.strlcpy/strlcat
. Można „nie lubić” ogólnej koncepcji łańcucha zakończonego zerem, ale nie o to chodzi. Jeśli znasz „wiele powodów, by nie lubićstrlcpy/strlcat
”, prawdopodobnie powinieneś napisać własną odpowiedź, zamiast oczekiwać, że będę w stanie czytać w myślach kogoś innego.strlcpy/strlcat
. Chociaż wydaje mi się, że rozumiem, o co w tym chodzi, osobiście odmawiam uznania tego za „problemy z bezpieczeństwem” w obrębie tradycyjnego języka C, jaki znam. To stwierdziłem w mojej odpowiedzi.Krytyka Ulricha opiera się na idei, że obcięcie ciągu znaków, które nie zostało wykryte przez program, może prowadzić do problemów z bezpieczeństwem, z powodu nieprawidłowej logiki. Dlatego, aby być bezpiecznym, musisz sprawdzić, czy nie ma obcięcia. Aby to zrobić dla konkatenacji ciągów znaków, oznacza to, że wykonujesz sprawdzenie według następującego wzoru:
if (destlen + sourcelen > dest_maxlen) { /* Bug out */ }
Teraz
strlcat
sprawnie robi to sprawdzenie, czy programista pamięta o sprawdzeniu wyniku - abyś mógł bezpiecznie z niego korzystać:if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen) { /* Bug out */ }
Ulrich chodzi o to, że skoro musisz mieć
destlen
isourcelen
wokół (lub przeliczyć je, costrlcat
skutecznie robi), równie dobrze możesz po prostu użyć bardziej wydajnegomemcpy
:if (destlen + sourcelen > dest_maxlen) { goto error_out; } memcpy(dest + destlen, source, sourcelen + 1); destlen += sourcelen;
(W powyższym kodzie
dest_maxlen
jest to maksymalna długość łańcucha, który może być przechowywanydest
- o jeden mniej niż rozmiardest
bufora.dest_bufferlen
To pełny rozmiardest buffer
).źródło
memcpy
niej może być dowolny typ pamięci i mam dodatkowego wymiaru, aby sprawdzić, próbując zrozumieć kod. Miałem starszą aplikację do debugowania, w której wszystko zostało zrobione z memcpy, to była prawdziwa PITA do poprawienia. Po przeniesieniu do dedykowanej funkcji String jest znacznie łatwiejszy do odczytania (i szybszy, ponieważ wiele niepotrzebnychstrlen
można usunąć).memcpy()
jest wystarczający (i potencjalnie bardziej wydajny niżstrcpy()
).memcpy()
to operacja na łańcuchu - w końcu została zadeklarowana w<string.h>
.Kiedy ludzie mówią „
strcpy()
jest niebezpieczny, użyjstrncpy()
zamiast tego” (lub podobne stwierdzenia dotyczącestrcat()
itp., Ale zamierzam sięstrcpy()
tutaj skupić), mają na myśli, że nie ma żadnych ograniczeństrcpy()
. Dlatego zbyt długi ciąg spowoduje przepełnienie bufora. Mają rację. Użyciestrncpy()
w tym przypadku zapobiegnie przepełnieniu bufora.Wydaje mi się, że to
strncpy()
naprawdę nie naprawia błędów: rozwiązuje problem, którego dobry programista może łatwo uniknąć.Jako programista C musisz znać rozmiar docelowy, zanim zaczniesz kopiować łańcuchy. Takie jest założenie
strncpy()
istrlcpy()
ostatnie parametry: dostarczasz im ten rozmiar. Możesz również poznać rozmiar źródła przed skopiowaniem ciągów. Następnie, jeśli miejsce docelowe nie jest wystarczająco duże, nie dzwoństrcpy()
. Albo zmień przydział bufora, albo zrób coś innego.Dlaczego mi się nie podoba
strncpy()
?strncpy()
w większości przypadków jest złym rozwiązaniem: Twój ciąg zostanie obcięty bez powiadomienia - wolałbym napisać dodatkowy kod, aby rozwiązać ten problem samodzielnie, a następnie podjąć działania, które chcę wykonać, zamiast pozwolić, aby jakaś funkcja zdecydowała o tym ja o tym, co robić.strncpy()
jest bardzo nieefektywny. Zapisuje do każdego bajtu w buforze docelowym. Nie potrzebujesz tych tysięcy'\0'
na końcu celu.'\0'
jeśli miejsce docelowe nie jest wystarczająco duże. Więc i tak musisz to zrobić sam. Złożoność tego nie jest warta zachodu.Teraz dochodzimy do
strlcpy()
. Zmiany w stosunkustrncpy()
do tego poprawiają, ale nie jestem pewien, czy specyficzne zachowaniestrl*
uzasadnia ich istnienie: są one zbyt szczegółowe. Nadal musisz znać rozmiar docelowy. Jest bardziej wydajna niżstrncpy()
dlatego, że niekoniecznie zapisuje do każdego bajtu w miejscu docelowym. Ale to rozwiązuje problem, który można rozwiązać wykonując:*((char *)mempcpy(dst, src, n)) = 0;
.Nie sądzę, żeby ktokolwiek to powiedział
strlcpy()
lubstrlcat()
mógł prowadzić do problemów z bezpieczeństwem, co oni (i ja) mówią, że mogą powodować błędy, na przykład, gdy spodziewasz się, że zamiast jego części zostanie napisany pełny ciąg.Głównym problemem jest tutaj: ile bajtów skopiować? Programista musi o tym wiedzieć, a jeśli tego nie zrobi,
strncpy()
albostrlcpy()
nie będzie go uratować.strlcpy()
istrlcat()
nie są standardowe, ani ISO C, ani POSIX. Dlatego ich użycie w programach przenośnych jest niemożliwe. W rzeczywistościstrlcat()
ma dwa różne warianty: implementacja Solaris różni się od innych dla przypadków skrajnych o długości 0. To sprawia, że jest jeszcze mniej użyteczna niż w innym przypadku.źródło
strlcpy
jest szybszy niżmemcpy
na wielu architekturach, zwłaszcza jeślimemcpy
kopiuje niepotrzebne dane końcowe.strlcpy
zwraca również ilość utraconych danych, co może pozwolić na szybsze odzyskanie danych przy mniejszej ilości kodu.n
aw moim wywołaniu memcpy jest dokładna liczba bajtów do zapisania, więc wydajność również nie stanowi większego problemu.strlcpy
każdym razem, gdy tego potrzebujesz,memcpy
istrlen
to też jest w porządku, ale po co kończyć nastrlcpy
, nie potrzebujesz teżmemcpy
funkcji, możesz skopiować bajty jeden po drugim. Implementacja referencyjna w normalnym przypadku wykonuje pętlę tylko raz, co jest lepsze dla większości architektur. Ale nawet jeśli najlepsza implementacja używastrlen
+memcpy
, to nadal nie ma powodu, aby nie musieć ponownie implementować bezpiecznego strcpy.strlcpy
, gdy przeglądasz ciąg wejściowy tylko raz w dobrym przypadku (który powinien być większością) i dwa razy w złym przypadku, za pomocą swojegostrlen
+memcpy
przechodzisz nad nim zawsze dwa razy. Jeśli to robi różnicę w praktyce, to inna historia.Myślę, że Ulrich i inni uważają, że da to fałszywe poczucie bezpieczeństwa. Przypadkowe obcięcie ciągów może mieć wpływ na bezpieczeństwo innych części kodu (na przykład, jeśli ścieżka systemu plików zostanie obcięta, program może nie wykonywać operacji na zamierzonym pliku).
źródło
malware.exe.jpg
domalware.exe
.Istnieją dwa „problemy” związane z używaniem funkcji strl:
Autorzy szkiców standardu c1x i Drepper twierdzą, że programiści nie sprawdzą zwracanej wartości. Drepper mówi, że powinniśmy w jakiś sposób znać długość i używać memcpy i całkowicie unikać funkcji łańcuchowych. Komitet standardów twierdzi, że bezpieczny strcpy powinien zwracać wartość niezerową po obcięciu, chyba że
_TRUNCATE
flaga stanowi inaczej . Chodzi o to, że ludzie częściej używają if (strncpy_s (...)).Niektórzy uważają, że funkcje łańcuchowe nigdy nie powinny się zawieszać, nawet po podaniu fałszywych danych. Wpływa to na standardowe funkcje, takie jak strlen, które w normalnych warunkach ulegają awarii. Nowy standard będzie zawierał wiele takich funkcji. Testy mają oczywiście spadek wydajności.
Zaletą proponowanych funkcji standardowych jest to, że możesz wiedzieć, ile danych przegapiłeś dzięki funkcjom strl .
źródło
strncpy_s
nie jest to bezpieczna wersja,strncpy
ale w zasadziestrlcpy
zamiennik.Nie sądzę
strlcpy
i uważam, żestrlcat
jest to niebezpieczne, a przynajmniej nie jest to powód, dla którego nie są one zawarte w glibc - w końcu glibc zawiera strncpy, a nawet strcpy.Krytyka, jaką otrzymali, była taka, że są rzekomo nieskuteczni, a nie niepewni .
Według dokumentu Secure Portability autorstwa Damiena Millera:
Dlatego nie są dostępne w glibc, ale nie jest prawdą, że nie są dostępne w systemie Linux. Są dostępne w systemie Linux w libbsd:
Są spakowane w Debianie i Ubuntu oraz innych dystrybucjach. Możesz też po prostu pobrać kopię i użyć w swoim projekcie - jest krótki i na licencji:
źródło
Bezpieczeństwo nie jest wartością logiczną. Funkcje C nie są całkowicie „bezpieczne” lub „niepewne”, „bezpieczne” lub „niebezpieczne”. W przypadku nieprawidłowego użycia prosta operacja przypisania w C może być „niepewna”. strlcpy () i strlcat () mogą być używane bezpiecznie (bezpiecznie) tak samo, jak strcpy () i strcat () mogą być bezpiecznie używane, gdy programista zapewnia niezbędne gwarancje prawidłowego użycia.
Głównym punktem z wszystkich tych funkcji ciągów C, standard i nie-tak-standard, to poziom, do którego one bezpieczne / bezpieczne użytkowanie łatwa . strcpy () i strcat () nie są trywialne w bezpiecznym użyciu; dowodzi tego liczba razy, kiedy programiści C popełnili błąd przez lata i pojawiły się paskudne luki i exploity. strlcpy () i strlcat () oraz strncpy () i strncat (), strncpy_s () i strncat_s (), są nieco łatwiejsze w użyciu, ale nadal nie są trywialne. Czy są niebezpieczne / niepewne? Nie więcej niż memcpy (), gdy jest używane nieprawidłowo.
źródło