FORTIFY_SOURCE

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.

What do these options do?

-fstack-protector

Consider the following C function:

void fun() {

char *buf = alloca(0x100);

/* 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:

08048404 <fun>:

push %ebp ; prologue

mov %esp,%ebp

 

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

 

leave ; epilogue

ret

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:

08048464 <fun>:

push %ebp ; prologue

mov %esp,%ebp

 

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

je 8048493 <fun+0x2f>

call 8048340 <__stack_chk_fail@plt>

 

leave ; epilogue

ret

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.

When the stack is overwritten and __stack_chk_fail branch is taken the program crashes with a message like this:

*** stack smashing detected ***: ./protected terminated

======= Backtrace: =========

/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0xf76da0e5]

/lib/i386-linux-gnu/libc.so.6(+0x10409a)[0xf76da09a]

./protected[0x80484de]

./protected[0x80483d7]

/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xf75ef4d3]

./protected[0x8048411]

======= Memory map: ========

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]

Aborted

-D_FORTIFY_SOURCE=2

Sample C code:

void fun(char *s) {

char buf[0x100];

strcpy(buf, s);

/* Don't allow gcc to optimise away the buf */

asm volatile("" :: "m" (buf));

}

Compiled without the code fortified, with -U_FORTIFY_SOURCE option:

08048450 <fun>:

push %ebp ; prologue

mov %esp,%ebp

 

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

call 8048320 <strcpy@plt>

 

leave ; epilogue

ret

With -D_FORTIFY_SOURCE=2:

08048470 <fun>:

push %ebp ; prologue

mov %esp,%ebp

 

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>

 

leave ; epilogue

ret

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 strcpymemcpymemset, etc.

Again, this prevents some buffer overflow attacks. Of course you should avoidstrcpy and always use strncpy, but it's worth noting that FORTIFY_SOURCE can also help with strncpy when GCC knows the destination buffer size. For example:

void fun(char *s, int l) {

char buf[0x100];

strncpy(buf, s, l);

asm volatile("" :: "m" (buf[0]));

}

Here GCC instead of calling strncpy(dst, src, l) will call__strncpy_chk(dst, src, l, 0x100) as GCC is aware of the size of the destination buffer.

When the buffer is overrun the program fails with a message very similar to the one seen previously. Instead of "stack smashing detected" you'll see "buffer overflow detected" headline:

*** buffer overflow detected ***: ./fortified terminated

======= Backtrace: =========

/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]

./fortified[0x8048488]

./fortified[0x80483a7]

/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xf75e84d3]

./fortified[0x80483e1]

======= Memory map: ========

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

fff8d000-fffae000 rw-p 00000000 00:00 0 [stack]

Aborted

 

SRC=https://idea.popcount.org/2013-08-15-fortify_source/

你可能感兴趣的:(source)