I am both confused and excited about this behavior I am getting from my C code. I do not understand how on earth this is happening! Before anything further, let's see the code-
#include <stdio.h>
int main(){
char string[2];
printf("Enter your string here: ");
gets(string);
printf("%s \n", string);
return 0;
}
It is very clear that- nothing is special here. Actually, this was an assignment of my Computer, Data & Network Security course, where I was supposed to demonstrate BufferOverflow.
It works just fine till 13 characters; 15 or more charters causes desired BufferOverflow. The disaster happens when I enter exactly 14 characters: the code starts behaving like a loop! It's like the main
function is being called again and again-
I am using CodeBlocks-16.01 and GNU GCC Compiler. I have also executed the code on TutorialsPoint but did not get this problem there.
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
.