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
This commit is contained in:
Maxim Ostapenko 2017-07-06 16:02:06 +00:00 committed by Maxim Ostapenko
parent b6f4312871
commit e3174bdf35
18 changed files with 468 additions and 4 deletions

View File

@ -1,3 +1,27 @@
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...

View File

@ -55,6 +55,7 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks.h"
#include "cfgloop.h"
#include "gimple-builder.h"
#include "gimple-fold.h"
#include "ubsan.h"
#include "params.h"
#include "builtins.h"
@ -245,6 +246,7 @@ along with GCC; see the file COPYING3. If not see
static unsigned HOST_WIDE_INT asan_shadow_offset_value;
static bool asan_shadow_offset_computed;
static vec<char *> sanitized_sections;
static tree last_alloca_addr;
/* Set of variable declarations that are going to be guarded by
use-after-scope sanitizer. */
@ -529,11 +531,186 @@ get_mem_ref_of_assignment (const gassign *assignment,
return true;
}
/* Return address of last allocated dynamic alloca. */
static tree
get_last_alloca_addr ()
{
if (last_alloca_addr)
return last_alloca_addr;
last_alloca_addr = create_tmp_reg (ptr_type_node, "last_alloca_addr");
gassign *g = gimple_build_assign (last_alloca_addr, null_pointer_node);
edge e = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun));
gsi_insert_on_edge_immediate (e, g);
return last_alloca_addr;
}
/* Insert __asan_allocas_unpoison (top, bottom) call after
__builtin_stack_restore (new_sp) call.
The pseudocode of this routine should look like this:
__builtin_stack_restore (new_sp);
top = last_alloca_addr;
bot = new_sp;
__asan_allocas_unpoison (top, bot);
last_alloca_addr = new_sp;
In general, we can't use new_sp as bot parameter because on some
architectures SP has non zero offset from dynamic stack area. Moreover, on
some architectures this offset (STACK_DYNAMIC_OFFSET) becomes known for each
particular function only after all callees were expanded to rtl.
The most noticeable example is PowerPC{,64}, see
http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#DYNAM-STACK.
To overcome the issue we use following trick: pass new_sp as a second
parameter to __asan_allocas_unpoison and rewrite it during expansion with
virtual_dynamic_stack_rtx later in expand_asan_emit_allocas_unpoison
function.
*/
static void
handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter)
{
if (!iter)
return;
tree last_alloca = get_last_alloca_addr ();
tree restored_stack = gimple_call_arg (call, 0);
tree fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCAS_UNPOISON);
gimple *g = gimple_build_call (fn, 2, last_alloca, restored_stack);
gsi_insert_after (iter, g, GSI_NEW_STMT);
g = gimple_build_assign (last_alloca, restored_stack);
gsi_insert_after (iter, g, GSI_NEW_STMT);
}
/* Deploy and poison redzones around __builtin_alloca call. To do this, we
should replace this call with another one with changed parameters and
replace all its uses with new address, so
addr = __builtin_alloca (old_size, align);
is replaced by
left_redzone_size = max (align, ASAN_RED_ZONE_SIZE);
Following two statements are optimized out if we know that
old_size & (ASAN_RED_ZONE_SIZE - 1) == 0, i.e. alloca doesn't need partial
redzone.
misalign = old_size & (ASAN_RED_ZONE_SIZE - 1);
partial_redzone_size = ASAN_RED_ZONE_SIZE - misalign;
right_redzone_size = ASAN_RED_ZONE_SIZE;
additional_size = left_redzone_size + partial_redzone_size +
right_redzone_size;
new_size = old_size + additional_size;
new_alloca = __builtin_alloca (new_size, max (align, 32))
__asan_alloca_poison (new_alloca, old_size)
addr = new_alloca + max (align, ASAN_RED_ZONE_SIZE);
last_alloca_addr = new_alloca;
ADDITIONAL_SIZE is added to make new memory allocation contain not only
requested memory, but also left, partial and right redzones as well as some
additional space, required by alignment. */
static void
handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter)
{
if (!iter)
return;
gassign *g;
gcall *gg;
const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
tree last_alloca = get_last_alloca_addr ();
tree callee = gimple_call_fndecl (call);
tree old_size = gimple_call_arg (call, 0);
tree ptr_type = gimple_call_lhs (call) ? TREE_TYPE (gimple_call_lhs (call))
: ptr_type_node;
tree partial_size = NULL_TREE;
bool alloca_with_align
= DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA_WITH_ALIGN;
unsigned int align
= alloca_with_align ? tree_to_uhwi (gimple_call_arg (call, 1)) : 0;
/* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
bytes of allocated space. Otherwise, align alloca to ASAN_RED_ZONE_SIZE
manually. */
align = MAX (align, ASAN_RED_ZONE_SIZE * BITS_PER_UNIT);
tree alloca_rz_mask = build_int_cst (size_type_node, redzone_mask);
tree redzone_size = build_int_cst (size_type_node, ASAN_RED_ZONE_SIZE);
/* Extract lower bits from old_size. */
wide_int size_nonzero_bits = get_nonzero_bits (old_size);
wide_int rz_mask
= wi::uhwi (redzone_mask, wi::get_precision (size_nonzero_bits));
wide_int old_size_lower_bits = wi::bit_and (size_nonzero_bits, rz_mask);
/* If alloca size is aligned to ASAN_RED_ZONE_SIZE, we don't need partial
redzone. Otherwise, compute its size here. */
if (wi::ne_p (old_size_lower_bits, 0))
{
/* misalign = size & (ASAN_RED_ZONE_SIZE - 1)
partial_size = ASAN_RED_ZONE_SIZE - misalign. */
g = gimple_build_assign (make_ssa_name (size_type_node, NULL),
BIT_AND_EXPR, old_size, alloca_rz_mask);
gsi_insert_before (iter, g, GSI_SAME_STMT);
tree misalign = gimple_assign_lhs (g);
g = gimple_build_assign (make_ssa_name (size_type_node, NULL), MINUS_EXPR,
redzone_size, misalign);
gsi_insert_before (iter, g, GSI_SAME_STMT);
partial_size = gimple_assign_lhs (g);
}
/* additional_size = align + ASAN_RED_ZONE_SIZE. */
tree additional_size = build_int_cst (size_type_node, align / BITS_PER_UNIT
+ ASAN_RED_ZONE_SIZE);
/* If alloca has partial redzone, include it to additional_size too. */
if (partial_size)
{
/* additional_size += partial_size. */
g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR,
partial_size, additional_size);
gsi_insert_before (iter, g, GSI_SAME_STMT);
additional_size = gimple_assign_lhs (g);
}
/* new_size = old_size + additional_size. */
g = gimple_build_assign (make_ssa_name (size_type_node), PLUS_EXPR, old_size,
additional_size);
gsi_insert_before (iter, g, GSI_SAME_STMT);
tree new_size = gimple_assign_lhs (g);
/* Build new __builtin_alloca call:
new_alloca_with_rz = __builtin_alloca (new_size, align). */
tree fn = builtin_decl_implicit (BUILT_IN_ALLOCA_WITH_ALIGN);
gg = gimple_build_call (fn, 2, new_size,
build_int_cst (size_type_node, align));
tree new_alloca_with_rz = make_ssa_name (ptr_type, gg);
gimple_call_set_lhs (gg, new_alloca_with_rz);
gsi_insert_before (iter, gg, GSI_SAME_STMT);
/* new_alloca = new_alloca_with_rz + align. */
g = gimple_build_assign (make_ssa_name (ptr_type), POINTER_PLUS_EXPR,
new_alloca_with_rz,
build_int_cst (size_type_node,
align / BITS_PER_UNIT));
gsi_insert_before (iter, g, GSI_SAME_STMT);
tree new_alloca = gimple_assign_lhs (g);
/* Poison newly created alloca redzones:
__asan_alloca_poison (new_alloca, old_size). */
fn = builtin_decl_implicit (BUILT_IN_ASAN_ALLOCA_POISON);
gg = gimple_build_call (fn, 2, new_alloca, old_size);
gsi_insert_before (iter, gg, GSI_SAME_STMT);
/* Save new_alloca_with_rz value into last_alloca to use it during
allocas unpoisoning. */
g = gimple_build_assign (last_alloca, new_alloca_with_rz);
gsi_insert_before (iter, g, GSI_SAME_STMT);
/* Finally, replace old alloca ptr with NEW_ALLOCA. */
replace_call_with_value (iter, new_alloca);
}
/* Return the memory references contained in a gimple statement
representing a builtin call that has to do with memory access. */
static bool
get_mem_refs_of_builtin_call (const gcall *call,
get_mem_refs_of_builtin_call (gcall *call,
asan_mem_ref *src0,
tree *src0_len,
bool *src0_is_store,
@ -544,7 +721,8 @@ get_mem_refs_of_builtin_call (const gcall *call,
tree *dst_len,
bool *dst_is_store,
bool *dest_is_deref,
bool *intercepted_p)
bool *intercepted_p,
gimple_stmt_iterator *iter = NULL)
{
gcc_checking_assert (gimple_call_builtin_p (call, BUILT_IN_NORMAL));
@ -603,6 +781,14 @@ get_mem_refs_of_builtin_call (const gcall *call,
len = gimple_call_lhs (call);
break;
case BUILT_IN_STACK_RESTORE:
handle_builtin_stack_restore (call, iter);
break;
case BUILT_IN_ALLOCA_WITH_ALIGN:
case BUILT_IN_ALLOCA:
handle_builtin_alloca (call, iter);
break;
/* And now the __atomic* and __sync builtins.
These are handled differently from the classical memory memory
access builtins above. */
@ -1363,6 +1549,28 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
return insns;
}
/* Emit __asan_allocas_unpoison (top, bot) call. The BASE parameter corresponds
to BOT argument, for TOP virtual_stack_dynamic_rtx is used. NEW_SEQUENCE
indicates whether we're emitting new instructions sequence or not. */
rtx_insn *
asan_emit_allocas_unpoison (rtx top, rtx bot, rtx_insn *before)
{
if (before)
push_to_sequence (before);
else
start_sequence ();
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), bot,
TYPE_MODE (pointer_sized_int_node));
do_pending_stack_adjust ();
rtx_insn *insns = get_insns ();
end_sequence ();
return insns;
}
/* Return true if DECL, a global var, might be overridden and needs
therefore a local alias. */
@ -2002,7 +2210,7 @@ instrument_builtin_call (gimple_stmt_iterator *iter)
&src0, &src0_len, &src0_is_store,
&src1, &src1_len, &src1_is_store,
&dest, &dest_len, &dest_is_store,
&dest_is_deref, &intercepted_p))
&dest_is_deref, &intercepted_p, iter))
{
if (dest_is_deref)
{
@ -3192,6 +3400,7 @@ asan_instrument (void)
if (shadow_ptr_types[0] == NULL_TREE)
asan_init_shadow_ptr_types ();
transform_statements ();
last_alloca_addr = NULL_TREE;
return 0;
}

View File

@ -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);

View File

@ -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 ();

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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"

View File

@ -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",

View File

@ -1,3 +1,14 @@
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

View File

@ -0,0 +1,22 @@
/* { 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)" */

View File

@ -0,0 +1,27 @@
/* { 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)" */

View File

@ -0,0 +1,21 @@
/* { 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;
}

View File

@ -0,0 +1,34 @@
/* { 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;
}

View File

@ -0,0 +1,22 @@
/* { 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)" */

View File

@ -0,0 +1,22 @@
/* { 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)" */

View File

@ -0,0 +1,15 @@
/* { 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;
}

View File

@ -0,0 +1,22 @@
/* { 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)" */