En als ik nu de code gebruik die jij gebruikt gaat het nog niet helemaal goed. Dan print hij bv. alleen de eerste 4karakters van de string die ik ingeef. Aangezien ik op internet wat ben aan het opzoeken geweest en erop uitkwam dat hij stopt bij het 5de karakter, mag ik dan de regel van fgets() vervangen door:
fgets(&x, 6, stdin);
of krijg ik dan ook een buffer overflow? Want dan slaagt hij toch 5karakters op als ik deze code gebruik? Of wordt het vijfde karakter nog wel opgeslagen maar niet geprint?
Dat komt door de (goede) manier waarop
fgets() werkt, en hoe strings in C werken.
Eerst over strings in C: stel dat we de string "Hallo!" hebben. Hoeveel karakters hebben we nodig om deze string op te slaan in het geheugen? Zes, lijkt het logische antwoord. Maar het échte antwoord is zeven. Waarom dan? Omdat C moet kunnen weten wanneer een string eindigt. Om dit te weten moet na het laatste karakter van een string nog een karakter genaamd de null-terminator ( '\0' ) opgeslagen worden die C vertelt dat dit het einde van de string is.
Om te zien hoe dit werkt raad ik je aan om gewoon eens het volgende stukje code te compileren en uit te voeren:
#include <stdio.h>
int main ( )
{
char test_string [ ] = "Hey! \n \0 Dit zal je nooit zien!";
printf ( test_string );
getchar ( );
return ( 0 );
}
Die null-terminator moeten we dus ook opslaan. Maar we mogen deze
niet buiten onze array opslaan, want dan krijgen we een buffer overflow en kunnen we problemen veroorzaken. Daarom moeten we voor de null-terminator plaats voorzien in onze array. Dit wil dus zeggen dat we een array moeten declareren met plaats voor alle karakters die willen opslaan
plus één karakter voor het opslaan van de null-terminator. Als we dus een string van vijf karakters willen opslaan, moeten we een character array van
zes elementen declareren, met één element voor de null-terminator.
Nu verder over
fgets():
fgets zal dus, met dit in achterhoofd, het aantal opgegeven karakters
min 1 lezen, en dan het laatste element van de array vulen met een null-terminator ( '\0' ) karakter ( of het zal lezen totdat het een newline karakter tegenkomt en dan het volgende element van de array vullen met een '\0' karakter, ook als is de array nog niet vol ).
Dus om jouw programma strings tot en met met vijf karakters groot te laten lezen, schrijf je het als volgt:
#include <stdio.h>
int main ( )
{
char x [ 6 ];
fgets( x, 6, stdin);
printf("adres: %x \nstring: %s \n", &x, x);
getchar ( );
return ( 0 );
}
(
kleine opmerking: je kunt inderdaad beter de '&' voor de x weghalen bij
fgets(), deze notatie werkt ook prima, maar wordt weinig gebruikt. )
@dropl: je zit echt helemaal verkeerd. In
theorie zou je enkel de eigen stack kunnen corrupt maken, inderdaad. Maar in de praktijk kun je als de buffer overflow groot genoeg is gewoon data van andere processen of zelfs het besturingssysteem overschrijven. Sterker nog, dat gaat zelfs heel goed, want de hele moderne hackerscene is op dat principe gebaseerd! Bijna alle exploits worden geschreven door een buffer overflow bug te vinden in software die via een netwerk communiceert en via deze bug genoeg data over de rand van de array heen te schrijven totdat je in de adress space van het besturingssysteem zit en je vandaar uit alle commando's kunt uitvoeren ( zoals het aanmaken van een rootaccount, het aanzetten van ssh, en alle processen die logbestanden bijhouden hiermee laten stoppen zodat een hacker toegang tot het systeem kan krijgen ). Buffer overflows zijn
zeer, zeer, zéér gevaarlijk. Een buffer overflow is geen klein mankement, het is het ergste soort lek wat in een programma kan zitten.