GCC isn't enabling D_FORTIFY_SOURCE, even with optimisation flag set (-O2)

1.6k views Asked by At

I've recently read about D_FORTIFY_SOURCE and the changes it makes to vulnerable functions. I wished to mess around with it, and as such made a small test binary.

The test binaries source code is:

  #include <stdio.h>
  #include <stdlib.h>
  //No ASLR/PIE
  //Enable FORTIFY
  //
  //
  //
  void shell()
  {
          system("sh");
  }
  
  int flag = 0;
  
  int main(int argc, char * argv[])
  {
          char buf [256];
          fgets(buf, sizeof(buf), stdin);
          printf(buf);
          if(flag)
          {
                  shell();
          }
  }

It wasn't meant to do anything, just to try out the protections on printf.

However, when I compiled the program, it didn't seem to be 'fortified'

The exact gcc command was:

gcc fsFORTIFIED.c -o fsFORTIFIED -m32 -no-pie -D_FORITFY_SOURCE=2 -O2

I made sure that the optimisation flag was set, yet the standard printf function remained. GDB gave the assembly of main:

   0x080491c6 <+0>: lea    ecx,[esp+0x4]
   0x080491ca <+4>: and    esp,0xfffffff0
   0x080491cd <+7>: push   DWORD PTR [ecx-0x4]
   0x080491d0 <+10>:    push   ebp
   0x080491d1 <+11>:    mov    ebp,esp
   0x080491d3 <+13>:    push   esi
   0x080491d4 <+14>:    push   ebx
   0x080491d5 <+15>:    push   ecx
   0x080491d6 <+16>:    sub    esp,0x120
   0x080491dc <+22>:    call   0x80490e0 <__x86.get_pc_thunk.bx>
   0x080491e1 <+27>:    add    ebx,0x2e1f
   0x080491e7 <+33>:    mov    eax,gs:0x14
   0x080491ed <+39>:    mov    DWORD PTR [ebp-0x1c],eax
   0x080491f0 <+42>:    xor    eax,eax
   0x080491f2 <+44>:    mov    eax,DWORD PTR [ebx-0x8]
   0x080491f8 <+50>:    push   DWORD PTR [eax]
   0x080491fa <+52>:    push   0x100
   0x080491ff <+57>:    lea    esi,[ebp-0x11c]
   0x08049205 <+63>:    push   esi
   0x08049206 <+64>:    call   0x8049050 <fgets@plt>
   0x0804920b <+69>:    mov    DWORD PTR [esp],esi
   0x0804920e <+72>:    call   0x8049040 <printf@plt>
   0x08049213 <+77>:    add    esp,0x10
   0x08049216 <+80>:    cmp    DWORD PTR [ebx+0x2c],0x0
   0x0804921d <+87>:    jne    0x804923b <main+117>
   0x0804921f <+89>:    mov    eax,DWORD PTR [ebp-0x1c]
   0x08049222 <+92>:    sub    eax,DWORD PTR gs:0x14
   0x08049229 <+99>:    jne    0x8049242 <main+124>
   0x0804922b <+101>:   mov    eax,0x0
   0x08049230 <+106>:   lea    esp,[ebp-0xc]
   0x08049233 <+109>:   pop    ecx
   0x08049234 <+110>:   pop    ebx
   0x08049235 <+111>:   pop    esi
   0x08049236 <+112>:   pop    ebp
   0x08049237 <+113>:   lea    esp,[ecx-0x4]
   0x0804923a <+116>:   ret    
   0x0804923b <+117>:   call   0x80491a6 <shell>
   0x08049240 <+122>:   jmp    0x804921f <main+89>
   0x08049242 <+124>:   call   0x80492d0 <__stack_chk_fail_local>

Here, I expected a call to printf_chk@plt, but instead it had printf@plt. To make sure, I ran the program, and gave it the input %3$x,and it ran fine instead of halting.

My question is, why isn't GCC properly implementing D_FORITFY_SOURCE, even after the optimisation flag is set?

Help is appreciated

1

There are 1 answers

1
Peter Cordes On

Make sure -D_FORTIFY_SOURCE=2 is spelled correctly.
(In this case, it wasn't: -D_FORITFY_SOURCE=2 was the problem.)

Unlike normal options, GCC can't catch typos in this for you.

-Dfoo=bar simply defines a preprocessor macro (GCC manual), exactly the same as #D_FORTIFY_SOURCE 2 at the top of your file. (-D is a standard compiler option that all compilers accept, not just GCC.) Only a few special macro names affect headers like stdio.h.

For options like -fstack-protector-strong, -march=skylake or -fno-math-errno or whatever, GCC does only accept valid options it recognizes. But of course -Dfoo=bar is valid for any foo, it just doesn't do anything because it's not the macro that glibc headers use in their #ifdefs.

The -D is spelled correctly; the rest of the option is just what's being defined.


In ISO C, global-scope names that begin with an _1 are reserved for use by the implementation, which is why defining _FORTIFY_SOURCE is allowed to be special. But GCC itself doesn't reject names like that. For GCC to do that, it would have to know all the names that the internals of glibc, MUSL, and other parts of "the C implementation" happen to use, and that would make it a pain to work on those headers.

Footnote 1: the reserved-names rules are somewhat less broad than that, I'm simplifying.