The short explanation is, just because you declared 'x' on the source line after 'buf', that doesn't mean the compiler put them next to each other on the stack. With the code shown, 'x' isn't used at all, so it probably didn't get put anywhere. Even if you did use 'x' somehow (and it would have to be a way that prevents it being stuffed into a register), there's a good chance the compiler will sort it below 'buf' precisely so that it does not get overwritten by code overflowing 'buf'.
You can force this program to overwrite 'x' with a struct
construct, e.g.
#include <stdio.h>
int main()
{
struct {
char buf[5];
char x[2];
} S = { { 0 }, { 'u' } };
printf("Please enter your name: ");
gets(S.buf);
printf("Hello %s!\n", S.buf);
printf("S.x[0] = %02x\n", S.x[0]);
return 0;
}
because the fields of a struct
are always laid out in memory in the order they appear in the source code.1 In principle there could be padding between S.buf
and S.x
, but char
must have an alignment requirement of 1, so the ABI probably doesn't require that.
But even if you do that, it won't print 'Hello stacku!', because gets
always writes a terminating NUL. Watch:
$ ./a.out
Please enter your name: stac
Hello stac!
S.x[0] = 75
$ ./a.out
Please enter your name: stack
Hello stack!
S.x[0] = 00
$ ./a.out
Please enter your name: stacks
Hello stacks!
S.x[0] = 73
See how it always prints the thing you typed, but x[0]
does get overwritten, first with a NUL, and then with an 's'?
(Have you already read Smashing the Stack for Fun and Profit? You should.)
1 Footnote for pedants: if bit-fields are involved, the order of fields in memory becomes partially implementation-defined. But that's not important for purposes of this question.