In recent years Linux distributions started treating security more seriously. Out of many security features two are directly affecting C programmers: -fstack-protector and -D_FORTIFY_SOURCE=2. These GCC options are now enabled by default on Ubuntu and Fedora.
Consider the following C function:
/* Don't allow gcc to optimise away the buf */
asm volatile("" :: "m" (buf));
Compiled without the stack protector, with -fno-stack-protector option, GCC produces the following assembly:
sub $0x128,%esp ; reserve 0x128B on the stack
lea 0xf(%esp),%eax ; eax = esp + 0xf
and $0xfffffff0,%eax ; align eax
mov %eax,-0xc(%ebp) ; save eax in the stack frame
On the other hand with -fstack-protector option GCC adds protection code to your functions that use alloca or have buffers larger than 8 bytes. Additional code ensures the stack did not overflow. Here's the generated assembly:
sub $0x128,%esp ; reserve 0x128B on the stack
mov %gs:0x14,%eax ; load stack canary using gs
mov %eax,-0xc(%ebp) ; save it in the stack frame
xor %eax,%eax ; clear the register
lea 0xf(%esp),%eax ; eax = esp + 0xf
and $0xfffffff0,%eax ; align eax
mov %eax,-0x10(%ebp) ; save eax in the stack frame
mov -0xc(%ebp),%eax ; load canary
xor %gs:0x14,%eax ; compare against one in gs
call 8048340 <__stack_chk_fail@plt>
After a function prologue a canary is loaded and saved into the stack. Later, just before the epilogue the canary is verified against the original. If the values don't match the program exits with an appropriate message. This can protect against some buffer overflow attacks. It incurs some performance penalty but it seems to be worth the benefit.
*** stack smashing detected ***: ./protected terminated
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0xf76da0e5]
/lib/i386-linux-gnu/libc.so.6(+0x10409a)[0xf76da09a]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xf75ef4d3]
08048000-08049000 r-xp 00000000 00:13 4058 ./protected
08049000-0804a000 r--p 00000000 00:13 4058 ./protected
0804a000-0804b000 rw-p 00001000 00:13 4058 ./protected
092e5000-09306000 rw-p 00000000 00:00 0 [heap]
f759e000-f75ba000 r-xp 00000000 08:01 161528 /lib/i386-linux-gnu/libgcc_s.so.1
f75ba000-f75bb000 r--p 0001b000 08:01 161528 /lib/i386-linux-gnu/libgcc_s.so.1
f75bb000-f75bc000 rw-p 0001c000 08:01 161528 /lib/i386-linux-gnu/libgcc_s.so.1
f75d5000-f75d6000 rw-p 00000000 00:00 0
f75d6000-f7779000 r-xp 00000000 08:01 161530 /lib/i386-linux-gnu/libc-2.15.so
f7779000-f777b000 r--p 001a3000 08:01 161530 /lib/i386-linux-gnu/libc-2.15.so
f777b000-f777c000 rw-p 001a5000 08:01 161530 /lib/i386-linux-gnu/libc-2.15.so
f777c000-f777f000 rw-p 00000000 00:00 0
f7796000-f779a000 rw-p 00000000 00:00 0
f779a000-f779b000 r-xp 00000000 00:00 0 [vdso]
f779b000-f77bb000 r-xp 00000000 08:01 161542 /lib/i386-linux-gnu/ld-2.15.so
f77bb000-f77bc000 r--p 0001f000 08:01 161542 /lib/i386-linux-gnu/ld-2.15.so
f77bc000-f77bd000 rw-p 00020000 08:01 161542 /lib/i386-linux-gnu/ld-2.15.so
ffeb2000-ffed3000 rw-p 00000000 00:00 0 [stack]
/* Don't allow gcc to optimise away the buf */
asm volatile("" :: "m" (buf));
Compiled without the code fortified, with -U_FORTIFY_SOURCE option:
sub $0x118,%esp ; reserve 0x118B on the stack
mov 0x8(%ebp),%eax ; load parameter `s` to eax
mov %eax,0x4(%esp) ; save parameter for strcpy
lea -0x108(%ebp),%eax ; count `buf` in eax
mov %eax,(%esp) ; save parameter for strcpy
sub $0x118,%esp ; reserve 0x118B on the stack
movl $0x100,0x8(%esp) ; save value 0x100 as parameter
mov 0x8(%ebp),%eax ; load parameter `s` to eax
mov %eax,0x4(%esp) ; save parameter for strcpy
lea -0x108(%ebp),%eax ; count `buf` in eax
mov %eax,(%esp) ; save parameter for strcpy
call 8048370 <__strcpy_chk@plt>
You can see GCC generated some additional code. This time instead of calling strcpy(dst, src) GCC automatically calls __strcpy_chk(dst, src, dstlen). With FORTIFY_SOURCE whenever possible GCC tries to uses buffer-length aware replacements for functions like strcpy, memcpy, memset, etc.
asm volatile("" :: "m" (buf[0]));
*** buffer overflow detected ***: ./fortified terminated
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0xf76d30e5]
/lib/i386-linux-gnu/libc.so.6(+0x102eba)[0xf76d1eba]
/lib/i386-linux-gnu/libc.so.6(+0x1021ed)[0xf76d11ed]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xf75e84d3]
08048000-08049000 r-xp 00000000 00:13 4208 ./fortified
08049000-0804a000 r--p 00000000 00:13 4208 ./fortified
0804a000-0804b000 rw-p 00001000 00:13 4208 ./fortified
08d6b000-08d8c000 rw-p 00000000 00:00 0 [heap]
f7597000-f75b3000 r-xp 00000000 08:01 161528 /lib/i386-linux-gnu/libgcc_s.so.1
f75b3000-f75b4000 r--p 0001b000 08:01 161528 /lib/i386-linux-gnu/libgcc_s.so.1
f75b4000-f75b5000 rw-p 0001c000 08:01 161528 /lib/i386-linux-gnu/libgcc_s.so.1
f75ce000-f75cf000 rw-p 00000000 00:00 0
f75cf000-f7772000 r-xp 00000000 08:01 161530 /lib/i386-linux-gnu/libc-2.15.so
f7772000-f7774000 r--p 001a3000 08:01 161530 /lib/i386-linux-gnu/libc-2.15.so
f7774000-f7775000 rw-p 001a5000 08:01 161530 /lib/i386-linux-gnu/libc-2.15.so
f7775000-f7778000 rw-p 00000000 00:00 0
f778f000-f7793000 rw-p 00000000 00:00 0
f7793000-f7794000 r-xp 00000000 00:00 0 [vdso]
f7794000-f77b4000 r-xp 00000000 08:01 161542 /lib/i386-linux-gnu/ld-2.15.so
f77b4000-f77b5000 r--p 0001f000 08:01 161542 /lib/i386-linux-gnu/ld-2.15.so
f77b5000-f77b6000 rw-p 00020000 08:01 161542 /lib/i386-linux-gnu/ld-2.15.so