Jeśli używasz sprintf() lub vsprintf(), trzeba przydzielić bufor pierwszy, i trzeba mieć pewność, że bufor jest wystarczająco duża, aby pomieścić co sprintf pisze. W przeciwnym razie sprintf z powodzeniem zastąpi pamięć znajdującą się poza końcem bufora.
char* x = malloc(5 * sizeof(char));
sprintf(x,"%s%s%s", "12", "34", "56"); // writes "123456" +null but overruns the buffer
... pisze „6” i kończącą null
poza koniec przestrzeni przydzielonej x
albo uszkadzając jakąś inną zmienną, lub powodując winy segmentacji.
Jeśli masz szczęście, będzie deptać pamięć pomiędzy przydzielonymi blokami i nie zaszkodzi - tym razem. Prowadzi to do sporadycznych błędów - najtrudniejszej do zdiagnozowania. Dobrze jest użyć narzędzia takiego jak ElectricFence, które powoduje szybkie awarie.
Nieuczciwy użytkownik, który dostarcza zbyt długie dane wejściowe, może spowodować, że program zachowuje się w nieoczekiwany sposób. Złośliwy użytkownik może wykorzystać to jako sposób na uzyskanie własnego kodu wykonywalnego w systemie.
Jednym z zabezpieczających przed tym jest użycie snprintf()
, który obcina łańcuch do maksymalnej podanej długości.
char *x = malloc(5 * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56"); // writes "1234" + null
wartość Zwrot size
jest długością że byłby napisany jeśli przestrzeń była dostępna - nie wliczając NULL kończącego.
W tym przypadku, jeśli size
jest większa lub równa 5, to wiesz, że wystąpiło obcięcie - a jeśli nie chcesz obcięcia, możesz przydzielić nowy ciąg i ponownie spróbować snprintf()
.
char *x = malloc(BUF_LEN * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56");
if(size >= BUF_LEN) {
realloc(&x,(size + 1) * sizeof(char));
snprintf(x, 5, "%s%s%s", "12", "34", "56");
}
(to jest algorytm dość naiwny, ale ilustruje punkt)
asprintf()
robi to w jednym kroku dla Ciebie - oblicza długość łańcucha, przyznaje, że ilość pamięci i zapisuje ciąg w tym.
char *x;
int size = asprintf(&x, "%s%s%s", "12", "34", "56");
We wszystkich przypadkach, gdy skończysz z x
trzeba go zwolnić, lub wyciek pamięci:
free(x);
asprintf()
jest niejawna malloc()
, więc trzeba sprawdzić to działało, tak jak z malloc()
lub dowolnym innym wywołaniem systemowym.
if(size == -1) {
/* deal with error in some way */
}
Zauważ, że asprintf()
jest częścią rozszerzenia GNU i BSD do libc - nie może być pewien, że będzie dostępny w każdym środowisku C. sprintf()
i snprintf()
są częścią standardów POSIX i C99.
'asprintf()' i 'vasprintf()' są rozszerzeniami GNU. Dodano tag GNU. – alk
Hmm, zastanawiam się, czy pytający wykonuje ćwiczenia tutaj: http://exploit-exercises.com/nebula/level02? – jordanpg
Bardzo dobry wpis na blogu na ten temat można znaleźć tutaj: [zarządzanie pamięcią-in-c-and-auto] (http://insanecoding.blogspot.de/2014/06/memory-management-in-c- i-auto.html) ... btw. kompletny blog wart jest przeczytania. – antibus