When and How to Use Gcc's Stack Protection Feature

When and how to use GCC's stack protection feature?

Stack-protection is a hardening strategy, not a debugging strategy. If your game is network-aware or otherwise has data coming from an uncontrolled source, turn it on. If it doesn't have data coming from somewhere uncontrolled, don't turn it on.

Here's how it plays out: If you have a bug and make a buffer change based on something an attacker can control, that attacker can overwrite the return address or similar portions of the stack to cause it to execute their code instead of your code. Stack protection will abort your program if it detects this happening. Your users won't be happy, but they won't be hacked either. This isn't the sort of hacking that is about cheating in the game, it's the sort of hacking that is about someone using a vulnerability in your code to create an exploit that potentially infects your user.

For debugging-oriented solutions, look at things like mudflap.

As to your specific questions:

  1. Use stack protector if you get data from uncontrolled sources. The answer to this is probably yes. So use it. Even if you don't have data from uncontrolled sources, you probably will eventually or already do and don't realize it.
  2. Stack protections for all buffers can be used if you want extra protection in exchange for some performance hit. From gcc4.4.2 manual:

    -fstack-protector


    Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call alloca, and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits.


    -fstack-protector-all


    Like -fstack-protector except that all functions are protected.

  3. The warnings tell you what buffers the stack protection can't protect.

  4. It is not necessarily suggesting you decrease your minimum buffer size, and at a size of 0/1, it is the same as stack-protector-all. It is only pointing it out to you so that you can, if you decide redesign the code so that buffer is protected.
  5. No, those warnings don't represent issues, they just point out information to you. Don't use them regularly.

How does gcc's -fstack-protector option prevent stack smashing?

This option does not prevent stack smashing, but rather detects it and halts the program.

From the gcc man page:

-fstack-protector

Emit extra code to check for buffer overflows, such as stack
smashing attacks. This is done by adding a guard variable to
functions with vulnerable objects. This includes functions that
call "alloca", and functions with buffers larger than 8 bytes. The
guards are initialized when a function is entered and then checked
when the function exits. If a guard check fails, an error message
is printed and the program exits.

You still have an overflow problem, but the addition of the guard variables is apparently masking the issue. If you run your program under valgrind it should be able to detect what's happening.

Stack Protection and Smashing using GCC

Am I miscalculating the position of the return address in the unprotected case?

That part is correct, at least as long as the address fits in an int. The correct type for retptr would be long with x86-64 asm, so that the pointer can hold a 64 bit address.

You could double check that by running the following program:

#include <stdio.h>

void function(int a, int b, int c)
{
char buf1[5];
char buf2[10];
int *retptr;

retptr = (void*)(buf2 + 40);
printf("retptr points to: %p\n", (long*)(long)*retptr);
(*retptr) += 8;
}

int main(void)
{
int x;

printf("ret address is %p\n", &&label);
x = 0;
function(1,2,3);
label:
x = 1;
printf("%d\n", x);

return 0;
}

By running this, you should be able to confirm that the address right after the function in is the one also held by retptr.

I believe that the reason you're not getting the expected 0, lies in this line:

(*retptr) += 8;

On my 64 bit system, x = 1 is compiled as:

  40058a:   c7 45 fc 01 00 00 00    movl   $0x1,-0x4(%rbp)
400591: 8b 45 fc mov -0x4(%rbp),%eax
400594: 89 c6 mov %eax,%esi

The first line loads 1 in x and the two other lines pass the value of x as an argument to printf(). Notice how that's 7 bytes, and not 8. If you change the increment to 7, you should see 0, as you expected.

Effectively, by adding 8, the ret instruction has setup the instruction pointer to point to 45, rather than 8b.
That code then becomes:

  45 fc                 rex.RB cld 
89 c6 mov %eax,%esi

I'm not entirely sure what happens at that point, and I suspect that depends on the CPU model. Mine appears to skip the instructions until mov %eax,%esi, and so printf displays the value of whatever %eax was. If you look at the disassembly of function(), it turns out that %rax is used to store the value of retptr, and that's the seemingly random value that gets printed.

What's the expected behavior of stack protection with statically-sized arrays?

The default on Ubuntu's gcc is -fstack-protector-strong, not -fstack-protector-all. (The gcc manual lists available options).

https://lwn.net/Articles/584225/ describes "strong". It really likes to protect arrays, but structs with no array members can't be indexed so there's much less chance of overflow.

On Godbolt, that produces stack protection for VecA but not VecB, like on your desktop. (And BTW, you can use g++ -S -masm=intel -O3 to get Intel-syntax, or objdump -drwC -Mintel, since you linked to Intel-syntax on Godbolt.)

You can use g++ -S -fverbose-asm -O3 foo.cpp to include comments showing all the implied and explicit options gcc used when compiling. On Godbolt, uncheck the // icon to not hide asm comments, like this.

# GNU C++14 (GCC-Explorer-Build) version 7.3.0 (x86_64-linux-gnu)
# compiled by GNU C version 5.4.0 20160609, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3, isl version isl-0.16.1-GMP

# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
# options passed: -fdiagnostics-color=always -imultiarch x86_64-linux-gnu
# -iprefix /opt/compiler-explorer/gcc-7.3.0/bin/../lib/gcc/x86_64-linux-gnu/7.3.0/
# -D_GNU_SOURCE
# /tmp/compiler-explorer-compiler11885-54-1u26why.tdoy/example.cpp
# -masm=intel -mtune=generic -march=x86-64
# -auxbase-strip /tmp/compiler-explorer-compiler11885-54-1u26why.tdoy/output.s
# -g -O3 -std=gnu++14 -fstack-protector-strong -fverbose-asm
# options enabled: -faggressive-loop-optimizations -falign-labels
# -fasynchronous-unwind-tables -fauto-inc-dec -fbranch-count-reg
# -fcaller-saves -fchkp-check-incomplete-type -fchkp-check-read
# -fchkp-check-write -fchkp-instrument-calls -fchkp-narrow-bounds
# -fchkp-optimize -fchkp-store-bounds -fchkp-use-static-bounds
# -fchkp-use-static-const-bounds -fchkp-use-wrappers -fcode-hoisting
# -fcombine-stack-adjustments -fcommon -fcompare-elim -fcprop-registers
# -fcrossjumping -fcse-follow-jumps -fdefer-pop
...

which option is stronger when protect functions?

-fstack-protector-all is "strongest", yes, but may be unnecessarily strong.

Roughly speaking:

  • -fstack-protector protects functions that use reasonably large stack buffers.

  • -fstack-protector-strong protects functions that use stack buffers of any size, even very small.

  • -fstack-protector-all protects all functions. This includes functions that do not use the stack at all and cannot possibly smash it, so in many cases it may impose a performance hit for no security gain.

For example:

void other_func(char *p);

void f1(void) {
}

void f2(void) {
char buf[16];
other_func(buf);
}

void f3(void) {
char c;
other_func(&c);
}

Using -fstack-protector adds protection to f2 only. -fstack-protector-strong adds it to f2 and f3. -fstack-protector-all adds it to all three functions, including f1 which previously consisted only of ret and had no possible way to overflow anything, but now becomes about 10 instructions long.

Try it on godbolt.

For more details, see the article "'Strong' stack protection for GCC" by Jake Edge, LWN.net, February 5, 2014.

Disabling stack protection in GCC not working

Linux follows the W^X principle: it marks memory pages as non-executable unless they are part of the code section. This goes beyond the scope of your compiled application, and for good reason. The OS assumes this responsibility to defend against buffer overflow attacks from any program executed on the system; especially programs that are actively attempting to perform a buffer overflow attacks, like yours.

You are attempting to write code on the stack via buf and overwrite the function's return address to jump execution into the newly injected code. When the function returns, the program counter is set to the overridden return address, which now points to stack memory. The SIGSEGV is thrown when the program counter attempts to execute the next instruction due to the revoked execute permissions on stack memory pages.

To execute from the stack, the OS stack protection must be disabled.

Fortunately Linux provides execstack for such a case to be used at the discretion of the user.

See this Unix & Linux Stack Exchange post for more general information.


Debugging Injected Data

If you are getting a SIGSEGV in gdb, it probably means that there is some error in your buffer overrun attempt. To avoid messing with the clean-up at the end of main, I would suggest making a function that you call from main to do your buffer overrun:

#include <stdio.h>
#include <string.h>

char injection_code[] = ""; // buffer overrun data here

void foo () {
char buf[100];

// memcpy used to copy the full injection string, including any nested 0s
memcpy(buf, injection_code, 108); // +8 here to handle 64-bit system RAs
}

int main (int argc, char** argv) {
foo();
printf("Done!\n");
return 0;
}

Use GDB to debug. I suggest putting a break point at the end of foo and checking the registers line up with what you'd expect with info registers. You'll probably be most interested in the instruction pointer, which you can check with info registers rip (64-bit) or info registers eip (32-bit).

You can explore what the stack looks like by using print or x/x in GDB. For instance, you can check $rbp+8 to see what the return address (RA) is on the stack.

GDB will SIGSEGV on the ret instruction if the RA points to an invalid location:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004005bc in foo () at bufferoverflow.c:27

You can check to see if it faulted on the ret instruction by checking the instruction pointer address at the time of fault (in the example above, it would be 0x00000000004005bc) against the program's assembly (use disassemble function_name in GDB).

If the return address points back in the stack, it may throw a SIGILL for an illegal instruction, which means your return address may not be aligned properly, or your injected instructions are is malformed:

Program received signal SIGILL, Illegal instruction.
0x00007fffffffdc13 in ?? ()

Again, you can use GDB to explore your stack to see why.

GDB is useful for getting the structure of the buffer overflow correct.
Once it works as intended however, remember that memory addresses may be shifted when executed outside of GDB, especially if you compile without the debug flag (-g). You will have to play around with the return address a little bit to get it where you want it in a 'live' environment.


An Aside

In general, buffer overflow attacks that directly run injected code are generally not feasible with today's stack protection mechanisms. Normally there needs to be additional vulnerabilities present to be able to execute arbitrary code.

GCC's Stack Protector Option and Higher CPU Usage

It should not cause a significant increase in CPU use. As the description states, stack protection involves putting a particular value on the stack at function entry and validating that it hasn't changed at function exit. Typically functions do a lot more than writing and reading one value, so this should not constitute significant overhead.

Stack protection should be used in conjunction with writing your code correctly. It can't catch every mistake and it can be worked around by attackers, but since it is automatically applied to every function it can help detect accidental or intentional overflows sooner.

What is the use of -fno-stack-protector?

In the standard/stock GCC, stack protector is off by default. However, some Linux distributions have patched GCC to turn it on by default. In my opinion, this is rather harmful, as it breaks the ability to compile anything that's not linked against the standard userspace libraries unless the Makefile specifically disables stack protector. It would even break the Linux kernel build except that the distributions with this hack added additional hacks to GCC to detect that the kernel is being built and disable it.



Related Topics



Leave a reply



Submit