Commit e3174bdf by Maxim Ostapenko Committed by Maxim Ostapenko

ASAN: Implement dynamic allocas/VLAs sanitization.

gcc/
	* asan.c: Include gimple-fold.h.
	(get_last_alloca_addr): New function.
	(handle_builtin_stackrestore): Likewise.
	(handle_builtin_alloca): Likewise.
	(asan_emit_allocas_unpoison): Likewise.
	(get_mem_refs_of_builtin_call): Add new parameter, remove const
	quallifier from first paramerer. Handle BUILT_IN_ALLOCA,
	BUILT_IN_ALLOCA_WITH_ALIGN and BUILT_IN_STACK_RESTORE builtins.
	(instrument_builtin_call): Pass gimple iterator to
	get_mem_refs_of_builtin_call.
	(last_alloca_addr): New global.
	* asan.h (asan_emit_allocas_unpoison): Declare.
	* builtins.c (expand_asan_emit_allocas_unpoison): New function.
	(expand_builtin): Handle BUILT_IN_ASAN_ALLOCAS_UNPOISON.
	* cfgexpand.c (expand_used_vars): Call asan_emit_allocas_unpoison
	if function calls alloca.
	* gimple-fold.c (replace_call_with_value): Remove static keyword.
	* gimple-fold.h (replace_call_with_value): Declare.
	* internal-fn.c: Include asan.h.
	* sanitizer.def (BUILT_IN_ASAN_ALLOCA_POISON,
	BUILT_IN_ASAN_ALLOCAS_UNPOISON): New builtins.

gcc/testsuite/
	* c-c++-common/asan/alloca_big_alignment.c: New test.
	* c-c++-common/asan/alloca_detect_custom_size.c: Likewise.
	* c-c++-common/asan/alloca_instruments_all_paddings.c: Likewise.
	* c-c++-common/asan/alloca_loop_unpoisoning.c: Likewise.
	* c-c++-common/asan/alloca_overflow_partial.c: Likewise.
	* c-c++-common/asan/alloca_overflow_right.c: Likewise.
	* c-c++-common/asan/alloca_safe_access.c: Likewise.
	* c-c++-common/asan/alloca_underflow_left.c: Likewise.

From-SVN: r250031
parent b6f43128
2017-07-06 Maxim Ostapenko <m.ostapenko@samsung.com>
* asan.c: Include gimple-fold.h.
(get_last_alloca_addr): New function.
(handle_builtin_stackrestore): Likewise.
(handle_builtin_alloca): Likewise.
(asan_emit_allocas_unpoison): Likewise.
(get_mem_refs_of_builtin_call): Add new parameter, remove const
quallifier from first paramerer. Handle BUILT_IN_ALLOCA,
BUILT_IN_ALLOCA_WITH_ALIGN and BUILT_IN_STACK_RESTORE builtins.
(instrument_builtin_call): Pass gimple iterator to
get_mem_refs_of_builtin_call.
(last_alloca_addr): New global.
* asan.h (asan_emit_allocas_unpoison): Declare.
* builtins.c (expand_asan_emit_allocas_unpoison): New function.
(expand_builtin): Handle BUILT_IN_ASAN_ALLOCAS_UNPOISON.
* cfgexpand.c (expand_used_vars): Call asan_emit_allocas_unpoison
if function calls alloca.
* gimple-fold.c (replace_call_with_value): Remove static keyword.
* gimple-fold.h (replace_call_with_value): Declare.
* internal-fn.c: Include asan.h.
* sanitizer.def (BUILT_IN_ASAN_ALLOCA_POISON,
BUILT_IN_ASAN_ALLOCAS_UNPOISON): New builtins.
2017-07-06 David Malcolm <dmalcolm@redhat.com>
* Makefile.in (SELFTEST_FLAGS): Drop "-x c", moving it to...
......
......@@ -25,6 +25,7 @@ extern void asan_function_start (void);
extern void asan_finish_file (void);
extern rtx_insn *asan_emit_stack_protection (rtx, rtx, unsigned int,
HOST_WIDE_INT *, tree *, int);
extern rtx_insn *asan_emit_allocas_unpoison (rtx, rtx, rtx_insn *);
extern bool asan_protect_global (tree);
extern void initialize_sanitizer_builtins (void);
extern tree asan_dynamic_init_call (bool);
......
......@@ -4962,6 +4962,26 @@ expand_builtin_alloca (tree exp)
return result;
}
/* Emit a call to __asan_allocas_unpoison call in EXP. Replace second argument
of the call with virtual_stack_dynamic_rtx because in asan pass we emit a
dummy value into second parameter relying on this function to perform the
change. See motivation for this in comment to handle_builtin_stack_restore
function. */
static rtx
expand_asan_emit_allocas_unpoison (tree exp)
{
tree arg0 = CALL_EXPR_ARG (exp, 0);
rtx top = expand_expr (arg0, NULL_RTX, GET_MODE (virtual_stack_dynamic_rtx),
EXPAND_NORMAL);
rtx ret = init_one_libfunc ("__asan_allocas_unpoison");
ret = emit_library_call_value (ret, NULL_RTX, LCT_NORMAL, ptr_mode, 2, top,
TYPE_MODE (pointer_sized_int_node),
virtual_stack_dynamic_rtx,
TYPE_MODE (pointer_sized_int_node));
return ret;
}
/* Expand a call to bswap builtin in EXP.
Return NULL_RTX if a normal call should be emitted rather than expanding the
function in-line. If convenient, the result should be placed in TARGET.
......@@ -6763,6 +6783,9 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
return target;
break;
case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
return expand_asan_emit_allocas_unpoison (exp);
case BUILT_IN_STACK_SAVE:
return expand_stack_save ();
......
......@@ -2241,6 +2241,11 @@ expand_used_vars (void)
expand_stack_vars (NULL, &data);
}
if ((flag_sanitize & SANITIZE_ADDRESS) && cfun->calls_alloca)
var_end_seq = asan_emit_allocas_unpoison (virtual_stack_dynamic_rtx,
virtual_stack_vars_rtx,
var_end_seq);
fini_vars_expansion ();
/* If there were any artificial non-ignored vars without rtl
......
......@@ -571,7 +571,7 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
/* Replace the call at *GSI with the gimple value VAL. */
static void
void
replace_call_with_value (gimple_stmt_iterator *gsi, tree val)
{
gimple *stmt = gsi_stmt (*gsi);
......
......@@ -58,6 +58,7 @@ extern bool gimple_fold_builtin_sprintf (gimple_stmt_iterator *);
extern bool gimple_fold_builtin_snprintf (gimple_stmt_iterator *);
extern bool arith_code_with_undefined_signed_overflow (tree_code);
extern gimple_seq rewrite_to_defined_overflow (gimple *);
extern void replace_call_with_value (gimple_stmt_iterator *, tree);
/* gimple_build, functionally matching fold_buildN, outputs stmts
int the provided sequence, matching and simplifying them on-the-fly.
......
......@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
#include "stor-layout.h"
#include "dojump.h"
#include "expr.h"
#include "asan.h"
#include "ubsan.h"
#include "recog.h"
#include "builtins.h"
......
......@@ -171,6 +171,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POISON_STACK_MEMORY,
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNPOISON_STACK_MEMORY,
"__asan_unpoison_stack_memory",
BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_ALLOCA_POISON, "__asan_alloca_poison",
BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_ALLOCAS_UNPOISON, "__asan_allocas_unpoison",
BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
/* Thread Sanitizer */
DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init",
......
2017-07-06 Maxim Ostapenko <m.ostapenko@samsung.com>
* c-c++-common/asan/alloca_big_alignment.c: New test.
* c-c++-common/asan/alloca_detect_custom_size.c: Likewise.
* c-c++-common/asan/alloca_instruments_all_paddings.c: Likewise.
* c-c++-common/asan/alloca_loop_unpoisoning.c: Likewise.
* c-c++-common/asan/alloca_overflow_partial.c: Likewise.
* c-c++-common/asan/alloca_overflow_right.c: Likewise.
* c-c++-common/asan/alloca_safe_access.c: Likewise.
* c-c++-common/asan/alloca_underflow_left.c: Likewise.
2017-07-06 Georg-Johann Lay <avr@gjlay.de>
PR target/81305
......
/* { dg-do run } */
/* { dg-shouldfail "asan" } */
#include <assert.h>
volatile int ten = 10;
__attribute__((noinline)) void foo(int index, int len) {
volatile char str[len] __attribute__((aligned(128)));
assert(!((long) str & 127L));
str[index] = '1'; // BOOM
}
int main() {
foo(ten, ten);
return 0;
}
/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*foo(\[^\n\r]*alloca_big_alignment.c:11|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*Address 0x\[0-9a-f\]+ is located in stack of thread T0.*(\n|\r\n|\r)" */
/* { dg-output "\[^\n\r]*in foo.*alloca_big_alignment.c.*(\n|\r\n|\r)" */
/* { dg-do run } */
/* { dg-shouldfail "asan" } */
#include <assert.h>
struct A {
char a[3];
int b[3];
};
volatile int ten = 10;
__attribute__((noinline)) void foo(int index, int len) {
volatile struct A str[len] __attribute__((aligned(32)));
assert(!((long) str & 31L));
str[index].a[0] = '1'; // BOOM
}
int main(int argc, char **argv) {
foo(ten, ten);
return 0;
}
/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*foo(\[^\n\r]*alloca_detect_custom_size.c:16|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*Address 0x\[0-9a-f\]+ is located in stack of thread T0.*(\n|\r\n|\r)" */
/* { dg-output "\[^\n\r]*in foo.*alloca_detect_custom_size.c.*(\n|\r\n|\r)" */
/* { dg-do run } */
#include "sanitizer/asan_interface.h"
#include <assert.h>
__attribute__((noinline)) void foo(int index, int len) {
volatile char str[len] __attribute__((aligned(32)));
assert(!((long) str & 31L));
char *q = (char *)__asan_region_is_poisoned((char *)str, 64);
assert(q && ((q - str) == index));
}
int main(int argc, char **argv) {
for (int i = 1; i < 33; ++i)
foo(i, i);
for (int i = 1; i < 33; ++i)
foo(i, i);
return 0;
}
/* { dg-do run } */
/* This testcase checks that allocas and VLAs inside loop are correctly unpoisoned. */
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "sanitizer/asan_interface.h"
void *top, *bot;
volatile int thirty_two = 32;
__attribute__((noinline)) void foo(int len) {
char x;
top = &x;
volatile char array[len];
assert(!((uintptr_t) array & 31L));
alloca(len);
for (int i = 0; i < thirty_two; ++i) {
char array[i];
bot = array;
/* Just to prevent optimization. */
printf("%p\n", bot);
assert(!((uintptr_t) bot & 31L));
}
}
int main(int argc, char **argv) {
foo(thirty_two);
void *q = __asan_region_is_poisoned(bot, (char *)top - (char *)bot);
assert(!q);
return 0;
}
/* { dg-do run } */
/* { dg-shouldfail "asan" } */
#include <assert.h>
volatile const int ten = 10;
__attribute__((noinline)) void foo(int index, int len) {
volatile char str[len] __attribute__((aligned(32)));
assert(!((long) str & 31L));
str[index] = '1'; // BOOM
}
int main(int argc, char **argv) {
foo(ten, ten);
return 0;
}
/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*foo(\[^\n\r]*alloca_overflow_partial.c:11|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*Address 0x\[0-9a-f\]+ is located in stack of thread T0.*(\n|\r\n|\r)" */
/* { dg-output "\[^\n\r]*in foo.*alloca_overflow_partial.c.*(\n|\r\n|\r)" */
/* { dg-do run } */
/* { dg-shouldfail "asan" } */
#include <assert.h>
volatile const int ten = 10;
__attribute__((noinline)) void foo(int index, int len) {
volatile char str[len] __attribute__((aligned(32)));
assert(!((long) str & 31L));
str[index] = '1'; // BOOM
}
int main(int argc, char **argv) {
foo(33, ten);
return 0;
}
/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*foo(\[^\n\r]*alloca_overflow_right.c:11|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*Address 0x\[0-9a-f\]+ is located in stack of thread T0.*(\n|\r\n|\r)" */
/* { dg-output "\[^\n\r]*in foo.*alloca_overflow_right.c.*(\n|\r\n|\r)" */
/* { dg-do run } */
#include <assert.h>
__attribute__((noinline)) void foo(int index, int len) {
volatile char str[len] __attribute__((aligned(32)));
assert(!((long)str & 31L));
str[index] = '1';
}
int main(int argc, char **argv) {
foo(4, 5);
foo(39, 40);
return 0;
}
/* { dg-do run } */
/* { dg-shouldfail "asan" } */
#include <assert.h>
volatile const int ten = 10;
__attribute__((noinline)) void foo(int index, int len) {
volatile char str[len] __attribute__((aligned(32)));
assert(!((long) str & 31L));
str[index] = '1'; // BOOM
}
int main(int argc, char **argv) {
foo(-1, ten);
return 0;
}
/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*foo(\[^\n\r]*alloca_underflow_left.c:11|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*Address 0x\[0-9a-f\]+ is located in stack of thread T0.*(\n|\r\n|\r)" */
/* { dg-output "\[^\n\r]*in foo.*alloca_underflow_left.c.*(\n|\r\n|\r)" */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment