13.2. Stack Smashing Detection¶
13.2.1. Introduction¶
The TI Arm Clang Compiler Tools (tiarmclang) support options to instrument protection against stack smashing attacks like buffer overflows.
13.2.2. Stack Smashing Detection Options¶
- -fstack-protector¶
Instructs the compiler to emit extra code to check for buffer overflows, such as stack-smashing attacks. This is done by adding a guard variable to vulnerable functions that contain certain types of objects. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error handling function is called. The error handling function can be made to indicate the error in some way and exit the program. Only variables that are actually allocated on the stack are considered, optimized away variables or variables allocated in registers are not considered.
Vulnerable functions for this setting are:
Functions with buffers or arrays larger than 8 bytes
Functions that call alloca() with parameters larger than 8 bytes
- -fstack-protector-strong¶
Instructs the compiler to behave as if -fstack-protector were specified, except that the vulnerable functions for which the compiler emits stack buffer overflow checking code are:
Functions that contain any array
Functions with any local variable that has its address taken
Functions that call to alloca()
- -fstack-protector-all¶
Instructs the compiler to behave as if -fstack-protector were specified, except that the compiler emits stack buffer overflow checking code for all functions instead of limiting protection as -fstack-protector and -fstack-protector-strong do.
13.2.2.1. Enabling Stack Smashing Detection¶
To enable stack smashing detection in your application, you need to provide definitions of:
__stack_chk_fail() - This function is called from an instrumented function when a check against the stack guard value, __stack_chk_guard, fails. A simple definition of this function might look like this:
void __stack_chk_fail(void) {
printf("__stack_chk_guard has been corrupted\n");
exit(0);
}
__stack_chk_guard - This is a globally visible symbol whose value can be copied into a location at the boundary of a function’s allocated stack on entry into the function, and loaded just prior to function exit to perform a check that the local copy of the __stack_chk_guard value has not been overwritten. A simple definition of this symbol might look like this:
unsigned long __stack_chk_guard = 0xbadeebad;
You can then compile a file containing both of these definitions to produce an object file that can be linked into an application that is instrumented for stack smashing detection.
13.2.2.2. Stack Smashing Detection Example¶
Here is a simple example to summarize and demonstrate how the stack smashing detection capability can be used:
The first source file presents the definitions of __stack_chk_fail() and __stack_chk_guard (stack_check.c):
#include <stdlib.h>
#include <stdio.h>
void __stack_chk_fail(void);
unsigned long __stack_chk_guard = 0xbadeebad;
void __stack_chk_fail(void) {
printf("ERROR: __stack_chk_guard has been corrupted\n");
eixit(0);
}
The second source file presents a use case where a function, foo, writes past the end of a local buffer (stack_smash.c):
#include <string.h>
void foo(void);
int main() {
foo();
return 0;
}
void foo(void) {
char buffer[3];
strcpy(buffer, "Oi! I am smashing your stack");
}
The stack_check.c source can then be compiled to generate stack_check.o:
%> tiarmclang -mcpu=cortex-m4 -c stack_check.c
and the stack_smash.c source file is compiled and linked with stack smashing detection enabled via the use of the -fstack-protector-all option:
%> tiarmclang -mcpu=cortex-m4 -fstack-protector-all stack_smash.c stack_check.o -o stack_smash.out -Wl,-llnk.cmd
When loaded and run, the following error message is emitted, and the program exits when the stack check fails before returning from foo:
ERROR: __stack_chk_guard has been corrupted