Your code generates a buffer overflow--a real one. Going past the end of string
can overwrite the return address [on the stack] that main
should return to when done.
If it is chosen correctly, it can loop back to main
or jump just about anywhere in memory. What it actually does depends upon the compiler, the linker, the loader, the address the program was loaded at.
And, the value of the string entered (i.e.) some strings will crash, others might loop, some might produce goofy results, but not loop. One string might do X behavior in a given environment and Y behavior in another. A different string might reverse these results.
What you really want to do is demonstrate (i.e. simulate) buffer overflow, without doing anything that will crash your program.
Here is a safe way to do this:
#include <stdio.h>
#define SEED 0xFF // sentinel value
// NOTE: using a struct guarantees that over will appear directly after string
// (i.e.) over is higher in memory than string
struct buffer {
char string[4]; // buffer that can overflow
unsigned char over[80]; // safe place for the overflow
};
int
main(void)
{
struct buffer buf;
int idx;
int over;
// prefill the "overflow detection buffer" with a sentinel value (e.g. one
// that can't be input via fgets [under normal circumstances])
for (idx = 0; idx < sizeof(buf.over); ++idx)
buf.over[idx] = SEED;
printf("Enter your string here: ");
fflush(stdout);
// NOTE: this fgets will never _really_ cause any harm -- the "10" slop
// factor guarantees this
fgets(buf.string,sizeof(buf) - 10,stdin);
// overflow is anything that ran past string into over
over = 0;
for (idx = 0; idx < sizeof(buf.over); ++idx) {
if (buf.over[idx] != SEED) {
over = 1;
break;
}
}
if (over)
printf("buffer overflowed\n");
else
printf("buffer did not overflow\n");
return 0;
}
UPDATE:
you should specifically admonish against the use of gets
Ordinarily, I would have. Because of the special nature of this question, I was on the fence about this.
(even though he wants a buffer overflow, you should add why he may very well get one he doesn't want using the removed gets
function)
IMO, this was implied by the use of fgets
in my example code, but may not have been specifically inferred. So, fair enough ...
In my example code, using gets(buf.string)
instead of the fgets
could/would produce the same [desired] effect. However, this would still be unsafe because there is still no limit on the length read. It could run past the total struct length sizeof(string) + sizeof(over)
and produce a real buffer overflow, just as before.
Since you were trying to cause a buffer overflow, it's easier to code with gets
, but you get the undesired behavior.
[As others have pointed out] gets
is deprecated for that very reason. If you just wanted a normal usage, replace gets(string)
with fgets(string,sizeof(string),stdin)
So, never use gets
and always use fgets
.