(See part 1)

Remember: you must compile with -02 if you want the checks to be effective


The idea behind FORTIFY_SOURCE is relatively simple: there are cases where the compiler can know the size of a buffer (if it’s a fixed sized buffer on the stack, as in the example, or if the buffer just came from a malloc() function call). With a known buffer size, functions that operate on the buffer can make sure the buffer will not overflow.


void foo(char *string)
    char buf[20];
    strcpy(buf, string);

Execution will fail:

[home ~/harden] ./bad $(perl -e 'print "A"x100')
zsh: segmentation fault  ./bad $(perl -e 'print "A"x100')

When compiling with -D_FORTIFY_SOURCE=2, gcc will add some checks to detect the overflow and terminate the program:

[home ~/harden] DEB_BUILD_HARDENING=1 make
[home ~/harden] ./bad $(perl -e 'print "A"x100')

*** buffer overflow detected ***: ./bad terminated
======= Backtrace: =========
======= Memory map:  ========
2ba8d1607000-2ba8d1622000 r-xp 00000000 03:01 468316                     /lib/ld-2.7.so
2ba8d1622000-2ba8d1625000 rw-p 2ba8d1622000 00:00 0 
2ba8d1821000-2ba8d1823000 rw-p 0001a000 03:01 468316                     /lib/ld-2.7.so
2ba8d1823000-2ba8d1961000 r-xp 00000000 03:01 471074                     /lib/libc-2.7.so
2ba8d1961000-2ba8d1b61000 ---p 0013e000 03:01 471074                     /lib/libc-2.7.so
2ba8d1b61000-2ba8d1b64000 r--p 0013e000 03:01 471074                     /lib/libc-2.7.so
2ba8d1b64000-2ba8d1b66000 rw-p 00141000 03:01 471074                     /lib/libc-2.7.so
2ba8d1b66000-2ba8d1b6c000 rw-p 2ba8d1b66000 00:00 0 
2ba8d1b6c000-2ba8d1b82000 r-xp 00000000 03:01 1045795                    /lib/libgcc_s.so.1
2ba8d1b82000-2ba8d1d82000 ---p 00016000 03:01 1045795                    /lib/libgcc_s.so.1
2ba8d1d82000-2ba8d1d83000 rw-p 00016000 03:01 1045795                    /lib/libgcc_s.so.1
555555554000-555555555000 r-xp 00000000 03:07 1225                       /home/pollux/harden/bad
555555754000-555555755000 r--p 00000000 03:07 1225                       /home/pollux/harden/bad
555555755000-555555756000 rw-p 00001000 03:07 1225                       /home/pollux/harden/bad
555555756000-555555777000 rw-p 555555756000 00:00 0                      [heap]
7fffd948e000-7fffd94a3000 rw-p 7ffffffea000 00:00 0                      [stack]
7fffd95fe000-7fffd9600000 r-xp 7fffd95fe000 00:00 0                      [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
zsh: abort      ./bad $(perl -e 'print "A"x100')

The difference is that there is no segmentation fault, because the program was properly terminated, so no exploit possible. A backtrace is also printed to help.

It is possible to detect if a program is using such checks by using objdump:

[home ~/harden] objdump -d bad | grep call |grep _chk
 84b:   e8 c0 fe ff ff          callq  710 <__stack_chk_fail@plt>

If there is one line or more, FORTIFY_SOURCE is active. However if the output is empty, FORTIFY_SOURCE either might not be enabled, or the program code is such that FORTIFY_SOURCE is not applicable (for example: secure code that has no static buffers and always checks buffer sizes, and thus FORTIFY_SOURCE cannot identify any potentially dangerous operations in the program).