asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.

* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
	* flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it
	into SANITIZE_UNDEFINED.
	* doc/invoke.texi: Describe -fsanitize=bounds.
	* gimplify.c (gimplify_call_expr): Add gimplification of internal
	functions created in the FEs.
	* internal-fn.c: Move "internal-fn.h" after "tree.h".
	(expand_UBSAN_BOUNDS): New function.
	* internal-fn.def (UBSAN_BOUNDS): New internal function.
	* internal-fn.h: Don't define internal functions here.
	* opts.c (common_handle_option): Add -fsanitize=bounds.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
	BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add.
	* tree-core.h: Define internal functions here.
	(struct tree_base): Add ifn field.
	* tree-pretty-print.c: Include "internal-fn.h".
	(dump_generic_node): Handle functions without CALL_EXPR_FN.
	* tree.c (get_callee_fndecl): Likewise.
	(build_call_expr_internal_loc): New function.
	* tree.def (CALL_EXPR): Update description.
	* tree.h (CALL_EXPR_IFN): Define.
	(build_call_expr_internal_loc): Declare.
	* ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic
	types.
	(ubsan_type_descriptor): Change bool parameter to enum
	ubsan_print_style.  Adjust the code.  Add handling of
	UBSAN_PRINT_ARRAY.
	(ubsan_expand_bounds_ifn): New function.
	(ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call.
	(ubsan_build_overflow_builtin): Likewise.
	(instrument_bool_enum_load): Likewise.
	(ubsan_instrument_float_cast): Likewise.
	* ubsan.h (enum ubsan_print_style): New enum.
	(ubsan_expand_bounds_ifn): Declare.
	(ubsan_type_descriptor): Adjust declaration.  Use a default parameter.
c-family/
	* c-gimplify.c: Include "c-ubsan.h" and "pointer-set.h".
	(ubsan_walk_array_refs_r): New function.
	(c_genericize): Instrument array bounds.
	* c-ubsan.c: Include "internal-fn.h".
	(ubsan_instrument_division): Mark instrumented arrays as having
	side effects.  Adjust ubsan_type_descriptor call.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_vla): Adjust ubsan_type_descriptor call.
	(ubsan_instrument_bounds): New function.
	(ubsan_array_ref_instrumented_p): New function.
	(ubsan_maybe_instrument_array_ref): New function.
	* c-ubsan.h (ubsan_instrument_bounds): Declare.
	(ubsan_array_ref_instrumented_p): Declare.
	(ubsan_maybe_instrument_array_ref): Declare.
testsuite/
	* c-c++-common/ubsan/bounds-1.c: New test.
	* c-c++-common/ubsan/bounds-2.c: New test.
	* c-c++-common/ubsan/bounds-3.c: New test.
	* c-c++-common/ubsan/bounds-4.c: New test.
	* c-c++-common/ubsan/bounds-5.c: New test.
	* c-c++-common/ubsan/bounds-6.c: New test.

From-SVN: r211859
This commit is contained in:
Marek Polacek 2014-06-20 21:20:51 +00:00
parent 87681fb550
commit 0e37a2f33d
28 changed files with 877 additions and 48 deletions

View File

@ -1,3 +1,41 @@
2014-06-20 Marek Polacek <polacek@redhat.com>
* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
* flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it
into SANITIZE_UNDEFINED.
* doc/invoke.texi: Describe -fsanitize=bounds.
* gimplify.c (gimplify_call_expr): Add gimplification of internal
functions created in the FEs.
* internal-fn.c: Move "internal-fn.h" after "tree.h".
(expand_UBSAN_BOUNDS): New function.
* internal-fn.def (UBSAN_BOUNDS): New internal function.
* internal-fn.h: Don't define internal functions here.
* opts.c (common_handle_option): Add -fsanitize=bounds.
* sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add.
* tree-core.h: Define internal functions here.
(struct tree_base): Add ifn field.
* tree-pretty-print.c: Include "internal-fn.h".
(dump_generic_node): Handle functions without CALL_EXPR_FN.
* tree.c (get_callee_fndecl): Likewise.
(build_call_expr_internal_loc): New function.
* tree.def (CALL_EXPR): Update description.
* tree.h (CALL_EXPR_IFN): Define.
(build_call_expr_internal_loc): Declare.
* ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic
types.
(ubsan_type_descriptor): Change bool parameter to enum
ubsan_print_style. Adjust the code. Add handling of
UBSAN_PRINT_ARRAY.
(ubsan_expand_bounds_ifn): New function.
(ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call.
(ubsan_build_overflow_builtin): Likewise.
(instrument_bool_enum_load): Likewise.
(ubsan_instrument_float_cast): Likewise.
* ubsan.h (enum ubsan_print_style): New enum.
(ubsan_expand_bounds_ifn): Declare.
(ubsan_type_descriptor): Adjust declaration. Use a default parameter.
2014-06-20 Maciej W. Rozycki <macro@codesourcery.com>
* config/rs6000/rs6000.md: Append `DONE' to preparation

View File

@ -2762,6 +2762,9 @@ pass_sanopt::execute (function *fun)
case IFN_UBSAN_NULL:
ubsan_expand_null_ifn (gsi);
break;
case IFN_UBSAN_BOUNDS:
ubsan_expand_bounds_ifn (&gsi);
break;
default:
break;
}
@ -2772,6 +2775,10 @@ pass_sanopt::execute (function *fun)
print_gimple_stmt (dump_file, stmt, 0, dump_flags);
fprintf (dump_file, "\n");
}
/* ubsan_expand_bounds_ifn might move us to the end of the BB. */
if (gsi_end_p (gsi))
break;
}
}
return 0;

View File

@ -1,4 +1,21 @@
2014-06-20 Hale Wang <hale.wang@arm.com>
2014-06-20 Marek Polacek <polacek@redhat.com>
* c-gimplify.c: Include "c-ubsan.h" and "pointer-set.h".
(ubsan_walk_array_refs_r): New function.
(c_genericize): Instrument array bounds.
* c-ubsan.c: Include "internal-fn.h".
(ubsan_instrument_division): Mark instrumented arrays as having
side effects. Adjust ubsan_type_descriptor call.
(ubsan_instrument_shift): Likewise.
(ubsan_instrument_vla): Adjust ubsan_type_descriptor call.
(ubsan_instrument_bounds): New function.
(ubsan_array_ref_instrumented_p): New function.
(ubsan_maybe_instrument_array_ref): New function.
* c-ubsan.h (ubsan_instrument_bounds): Declare.
(ubsan_array_ref_instrumented_p): Declare.
(ubsan_maybe_instrument_array_ref): Declare.
2014-06-20 Hale Wang <hale.wang@arm.com>
PR lto/61123
* c.opt (fshort-enums): Add to LTO.

View File

@ -45,6 +45,8 @@ along with GCC; see the file COPYING3. If not see
#include "c-pretty-print.h"
#include "cgraph.h"
#include "cilk.h"
#include "c-ubsan.h"
#include "pointer-set.h"
/* The gimplification pass converts the language-dependent trees
(ld-trees) emitted by the parser into language-independent trees
@ -67,6 +69,39 @@ along with GCC; see the file COPYING3. If not see
walk back up, we check that they fit our constraints, and copy them
into temporaries if not. */
/* Callback for c_genericize. */
static tree
ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
{
struct pointer_set_t *pset = (struct pointer_set_t *) data;
/* Since walk_tree doesn't call the callback function on the decls
in BIND_EXPR_VARS, we have to walk them manually. */
if (TREE_CODE (*tp) == BIND_EXPR)
{
for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl))
{
if (TREE_STATIC (decl))
{
*walk_subtrees = 0;
continue;
}
walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, pset,
pset);
walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, pset, pset);
walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, pset,
pset);
}
}
else if (TREE_CODE (*tp) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (*tp, 0)) == ARRAY_REF)
ubsan_maybe_instrument_array_ref (&TREE_OPERAND (*tp, 0), true);
else if (TREE_CODE (*tp) == ARRAY_REF)
ubsan_maybe_instrument_array_ref (tp, false);
return NULL_TREE;
}
/* Gimplification of statement trees. */
/* Convert the tree representation of FNDECL from C frontend trees to
@ -79,6 +114,14 @@ c_genericize (tree fndecl)
int local_dump_flags;
struct cgraph_node *cgn;
if (flag_sanitize & SANITIZE_BOUNDS)
{
struct pointer_set_t *pset = pointer_set_create ();
walk_tree (&DECL_SAVED_TREE (fndecl), ubsan_walk_array_refs_r, pset,
pset);
pointer_set_destroy (pset);
}
/* Dump the C-specific tree IR. */
dump_orig = dump_begin (TDI_original, &local_dump_flags);
if (dump_orig)
@ -207,16 +250,16 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED,
}
break;
}
case CILK_SPAWN_STMT:
gcc_assert
(fn_contains_cilk_spawn_p (cfun)
gcc_assert
(fn_contains_cilk_spawn_p (cfun)
&& cilk_detect_spawn_and_unwrap (expr_p));
/* If errors are seen, then just process it as a CALL_EXPR. */
if (!seen_error ())
return (enum gimplify_status) gimplify_cilk_spawn (expr_p);
case MODIFY_EXPR:
case INIT_EXPR:
case CALL_EXPR:

View File

@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/c-common.h"
#include "c-family/c-ubsan.h"
#include "asan.h"
#include "internal-fn.h"
/* Instrument division by zero and INT_MIN / -1. If not instrumenting,
return NULL_TREE. */
@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
return NULL_TREE;
/* In case we have a SAVE_EXPR in a conditional context, we need to
make sure it gets evaluated before the condition. */
make sure it gets evaluated before the condition. If the OP0 is
an instrumented array reference, mark it as having side effects so
it's not folded away. */
if (flag_sanitize & SANITIZE_BOUNDS)
{
tree xop0 = op0;
while (CONVERT_EXPR_P (xop0))
xop0 = TREE_OPERAND (xop0, 0);
if (TREE_CODE (xop0) == ARRAY_REF)
{
TREE_SIDE_EFFECTS (xop0) = 1;
TREE_SIDE_EFFECTS (op0) = 1;
}
}
t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
if (flag_sanitize_undefined_trap_on_error)
tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
else
{
tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
ubsan_type_descriptor (type, false),
NULL_TREE);
ubsan_type_descriptor (type), NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode
= flag_sanitize_recover
@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
return NULL_TREE;
/* In case we have a SAVE_EXPR in a conditional context, we need to
make sure it gets evaluated before the condition. */
make sure it gets evaluated before the condition. If the OP0 is
an instrumented array reference, mark it as having side effects so
it's not folded away. */
if (flag_sanitize & SANITIZE_BOUNDS)
{
tree xop0 = op0;
while (CONVERT_EXPR_P (xop0))
xop0 = TREE_OPERAND (xop0, 0);
if (TREE_CODE (xop0) == ARRAY_REF)
{
TREE_SIDE_EFFECTS (xop0) = 1;
TREE_SIDE_EFFECTS (op0) = 1;
}
}
t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t,
tt ? tt : integer_zero_node);
@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
else
{
tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL,
ubsan_type_descriptor (type0, false),
ubsan_type_descriptor (type1, false),
NULL_TREE);
ubsan_type_descriptor (type0),
ubsan_type_descriptor (type1), NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode
@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size)
else
{
tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL,
ubsan_type_descriptor (type, false),
NULL_TREE);
ubsan_type_descriptor (type), NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode
= flag_sanitize_recover
@ -228,3 +251,102 @@ ubsan_instrument_return (location_t loc)
tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN);
return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
}
/* Instrument array bounds for ARRAY_REFs. We create special builtin,
that gets expanded in the sanopt pass, and make an array dimension
of it. ARRAY is the array, *INDEX is an index to the array.
Return NULL_TREE if no instrumentation is emitted.
IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */
tree
ubsan_instrument_bounds (location_t loc, tree array, tree *index,
bool ignore_off_by_one)
{
tree type = TREE_TYPE (array);
tree domain = TYPE_DOMAIN (type);
if (domain == NULL_TREE)
return NULL_TREE;
tree bound = TYPE_MAX_VALUE (domain);
if (ignore_off_by_one)
bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound,
build_int_cst (TREE_TYPE (bound), 1));
/* Detect flexible array members and suchlike. */
tree base = get_base_address (array);
if (base && (TREE_CODE (base) == INDIRECT_REF
|| TREE_CODE (base) == MEM_REF))
{
tree next = NULL_TREE;
tree cref = array;
/* Walk all structs/unions. */
while (TREE_CODE (cref) == COMPONENT_REF)
{
if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE)
for (next = DECL_CHAIN (TREE_OPERAND (cref, 1));
next && TREE_CODE (next) != FIELD_DECL;
next = DECL_CHAIN (next))
;
if (next)
/* Not a last element. Instrument it. */
break;
/* Ok, this is the last field of the structure/union. But the
aggregate containing the field must be the last field too,
recursively. */
cref = TREE_OPERAND (cref, 0);
}
if (!next)
/* Don't instrument this flexible array member-like array in non-strict
-fsanitize=bounds mode. */
return NULL_TREE;
}
*index = save_expr (*index);
/* Create a "(T *) 0" tree node to describe the array type. */
tree zero_with_type = build_int_cst (build_pointer_type (type), 0);
return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS,
void_type_node, 3, zero_with_type,
*index, bound);
}
/* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS. */
bool
ubsan_array_ref_instrumented_p (const_tree t)
{
if (TREE_CODE (t) != ARRAY_REF)
return false;
tree op1 = TREE_OPERAND (t, 1);
return TREE_CODE (op1) == COMPOUND_EXPR
&& TREE_CODE (TREE_OPERAND (op1, 0)) == CALL_EXPR
&& CALL_EXPR_FN (TREE_OPERAND (op1, 0)) == NULL_TREE
&& CALL_EXPR_IFN (TREE_OPERAND (op1, 0)) == IFN_UBSAN_BOUNDS;
}
/* Instrument an ARRAY_REF, if it hasn't already been instrumented.
IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */
void
ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one)
{
if (!ubsan_array_ref_instrumented_p (*expr_p)
&& current_function_decl != NULL_TREE
&& !lookup_attribute ("no_sanitize_undefined",
DECL_ATTRIBUTES (current_function_decl)))
{
tree op0 = TREE_OPERAND (*expr_p, 0);
tree op1 = TREE_OPERAND (*expr_p, 1);
tree e = ubsan_instrument_bounds (EXPR_LOCATION (*expr_p), op0, &op1,
ignore_off_by_one);
if (e != NULL_TREE)
{
tree t = copy_node (*expr_p);
TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
e, op1);
*expr_p = t;
}
}
}

View File

@ -25,5 +25,8 @@ extern tree ubsan_instrument_division (location_t, tree, tree);
extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
extern tree ubsan_instrument_vla (location_t, tree);
extern tree ubsan_instrument_return (location_t);
extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
extern bool ubsan_array_ref_instrumented_p (const_tree);
extern void ubsan_maybe_instrument_array_ref (tree *, bool);
#endif /* GCC_C_UBSAN_H */

View File

@ -5448,6 +5448,13 @@ This option enables floating-point type to integer conversion checking.
We check that the result of the conversion does not overflow.
This option does not work well with @code{FE_INVALID} exceptions enabled.
@item -fsanitize=bounds
@opindex fsanitize=bounds
This option enables instrumentation of array bounds. Various out of bounds
accesses are detected. Flexible array members are not instrumented, as well
as initializers of variables with static storage.
@item -fsanitize-recover
@opindex fsanitize-recover
By default @option{-fsanitize=undefined} sanitization (and its suboptions

View File

@ -230,9 +230,11 @@ enum sanitize_code {
SANITIZE_ENUM = 1 << 11,
SANITIZE_FLOAT_DIVIDE = 1 << 12,
SANITIZE_FLOAT_CAST = 1 << 13,
SANITIZE_BOUNDS = 1 << 14,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
| SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM,
| SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
| SANITIZE_BOUNDS,
SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
};

View File

@ -2264,6 +2264,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
if (! EXPR_HAS_LOCATION (*expr_p))
SET_EXPR_LOCATION (*expr_p, input_location);
/* Gimplify internal functions created in the FEs. */
if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
{
nargs = call_expr_nargs (*expr_p);
enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
auto_vec<tree> vargs (nargs);
for (i = 0; i < nargs; i++)
{
gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
EXPR_LOCATION (*expr_p));
vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
}
gimple call = gimple_build_call_internal_vec (ifn, vargs);
gimplify_seq_add_stmt (pre_p, call);
return GS_ALL_DONE;
}
/* This may be a call to a builtin function.
Builtin function calls may be transformed into different

View File

@ -20,8 +20,8 @@ along with GCC; see the file COPYING3. If not see
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "internal-fn.h"
#include "tree.h"
#include "internal-fn.h"
#include "stor-layout.h"
#include "expr.h"
#include "optabs.h"
@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
gcc_unreachable ();
}
/* This should get expanded in the sanopt pass. */
static void
expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
{
gcc_unreachable ();
}
/* Add sub/add overflow checking to the statement STMT.
CODE says whether the operation is +, or -. */

View File

@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF)
DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF)
DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW)

View File

@ -20,13 +20,6 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_INTERNAL_FN_H
#define GCC_INTERNAL_FN_H
enum internal_fn {
#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
#include "internal-fn.def"
#undef DEF_INTERNAL_FN
IFN_LAST
};
/* Return the name of internal function FN. The name is only meaningful
for dumps; it has no linkage. */

View File

@ -1467,6 +1467,7 @@ common_handle_option (struct gcc_options *opts,
sizeof "float-divide-by-zero" - 1 },
{ "float-cast-overflow", SANITIZE_FLOAT_CAST,
sizeof "float-cast-overflow" - 1 },
{ "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
{ NULL, 0, 0 }
};
const char *comma;

View File

@ -409,3 +409,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
"__ubsan_handle_float_cast_overflow_abort",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
"__ubsan_handle_out_of_bounds",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT,
"__ubsan_handle_out_of_bounds_abort",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)

View File

@ -1,3 +1,12 @@
2014-06-20 Marek Polacek <polacek@redhat.com>
* c-c++-common/ubsan/bounds-1.c: New test.
* c-c++-common/ubsan/bounds-2.c: New test.
* c-c++-common/ubsan/bounds-3.c: New test.
* c-c++-common/ubsan/bounds-4.c: New test.
* c-c++-common/ubsan/bounds-5.c: New test.
* c-c++-common/ubsan/bounds-6.c: New test.
2014-06-20 Yufeng Zhang <yufeng.zhang@arm.com>
Make the tests big-endian friendly.

View File

@ -0,0 +1,75 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=bounds -fno-sanitize-recover -Wall" } */
/* Don't fail on valid uses. */
struct S { int a[10]; };
struct T { int l; int a[]; };
struct U { int l; int a[0]; };
__attribute__ ((noinline, noclone))
void
fn_p (int p)
{
}
__attribute__ ((noinline, noclone))
void
fn_a (volatile int a[])
{
/* This is not instrumented. */
a[4] = 5;
}
__attribute__ ((noinline, noclone))
int
foo_i (int i)
{
int a[5] = { };
int k = i ? a[i] : i;
return k;
}
int
main (void)
{
volatile int a[5];
a[4] = 1;
a[2] = a[3];
fn_p (a[4]);
fn_a (a);
int i = 4;
a[i] = 1;
a[2] = a[i];
fn_p (a[i]);
foo_i (i);
const int n = 5;
volatile int b[n];
b[4] = 1;
b[2] = b[3];
fn_p (b[4]);
fn_a (b);
volatile int c[n][n][n];
c[2][2][2] = 2;
i = c[4][4][4];
volatile struct S s;
s.a[9] = 1;
i = s.a[9];
/* Don't instrument flexible array members. */
struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10);
t->a[1] = 1;
struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10);
u->a[1] = 1;
long int *d[10][5];
d[9][0] = (long int *) 0;
d[8][3] = d[9][0];
return 0;
}

View File

@ -0,0 +1,168 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
/* Test runtime errors. */
struct S { int a[10]; };
int
foo_5 (void)
{
return 5;
}
__attribute__ ((noinline, noclone))
void
fn_p (int p)
{
(void) p;
}
static void __attribute__ ((noinline, noclone))
fn1 (void)
{
volatile int a[5];
a[5] = 1;
a[2] = a[5];
}
static void __attribute__ ((noinline, noclone))
fn2 (void)
{
volatile int a[5];
int i = 5;
int *p = &i;
a[*p] = 1;
}
static void __attribute__ ((noinline, noclone))
fn3 (void)
{
volatile int a[5];
fn_p (a[5]);
}
static void __attribute__ ((noinline, noclone))
fn4 (void)
{
volatile int a[5];
a[foo_5 ()] = 1;
}
static void __attribute__ ((noinline, noclone))
fn5 (void)
{
int i = 5;
volatile int a[i];
a[i] = 1;
a[2] = a[i];
}
static void __attribute__ ((noinline, noclone))
fn6 (void)
{
int i = 5;
volatile int a[i];
fn_p (a[i]);
a[foo_5 ()] = 1;
}
static void __attribute__ ((noinline, noclone))
fn7 (void)
{
int n = 5, i;
volatile int c[n][n][n];
c[5][2][2] = 2;
c[2][5][2] = 2;
c[2][2][5] = 2;
i = c[5][2][2];
i = c[2][5][2];
i = c[2][2][5];
}
static void __attribute__ ((noinline, noclone))
fn8 (void)
{
int i = 5;
volatile struct S s;
s.a[10] = 1;
i = s.a[10];
}
static void __attribute__ ((noinline, noclone))
fn9 (void)
{
long int *volatile d[10][5];
d[10][0] = 0;
d[8][3] = d[10][0];
}
static void __attribute__ ((noinline, noclone))
fn10 (void)
{
/* Beware of side-effects. */
volatile int x = 10;
volatile int e[20];
e[x++] = 3;
if (x != 11)
__builtin_abort ();
e[x--] = 3;
if (x != 10)
__builtin_abort ();
}
static void __attribute__ ((noinline, noclone))
fn11 (void)
{
char ***volatile f[5];
f[5] = 0;
f[2] = f[5];
}
static void __attribute__ ((noinline, noclone))
fn12 (int i)
{
volatile int a[5] = { };
int k = i ? a[i] : i;
}
int
main (void)
{
fn1 ();
fn2 ();
fn3 ();
fn4 ();
fn5 ();
fn6 ();
fn7 ();
fn8 ();
fn9 ();
fn10 ();
fn11 ();
fn12 (5);
return 0;
}
/* { dg-output "index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */

View File

@ -0,0 +1,23 @@
/* { dg-do compile } */
/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
/* Do not generate invalid diagnostics. */
extern const int a[10];
extern int bar (int);
void
foo (int i, int j)
{
bar (a[i] >> j);
bar ((unsigned long) a[i] >> j);
bar ((short int) (unsigned long) a[i] >> j);
bar (j >> a[i]);
bar (j >> (unsigned long) a[i]);
bar (j >> (short int) (unsigned long) a[i]);
bar (a[i] / j);
bar ((unsigned long) a[i] / j);
bar ((short int) (unsigned long) a[i] / j);
bar (j / a[i]);
bar (j / (unsigned long) a[i]);
bar (j / (short int) (unsigned long) a[i]);
}

View File

@ -0,0 +1,17 @@
/* { dg-do compile } */
/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused" } */
/* Initializers of TREE_STATICs aren't instrumented.
But don't ICE on 'em. */
int A[2];
int *gp = &A[4];
int *gpi;
int
main (void)
{
gpi = &A[4];
static int *pi = &A[4];
return 0;
}

View File

@ -0,0 +1,88 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
/* Test flexible array member-like arrays. Normal flexible array members
are tested in bounds-1.c. Test non-strict mode. */
__attribute__ ((noinline, noclone))
void
fn1 (void)
{
volatile struct S { char a[1]; char b; } s;
s.a[0] = 1; // OK
s.a[1] = 2; // error
volatile struct S *p = &s;
p->a[0] = 1; // OK
p->a[1] = 1; // error
}
__attribute__ ((noinline, noclone))
void
fn2 (void)
{
struct S { int c; char d[4]; };
volatile struct T { int e; struct S f; int g; } t;
t.f.d[3] = 1; // OK
t.f.d[4] = 1; // error
volatile struct T *p = &t;
p->f.d[3] = 1; // OK
p->f.d[4] = 1; // error
}
__attribute__ ((noinline, noclone))
void
fn3 (void)
{
volatile struct S { char b; char a[1]; } s;
s.a[0] = 1; // OK
s.a[1] = 1; // error
volatile struct S *p = &s;
p->a[0] = 1; // OK
p->a[1] = 1; // error in strict mode
}
__attribute__ ((noinline, noclone))
void
fn4 (void)
{
volatile struct S { char b; char a[1]; } s;
volatile struct T { struct S s; int i; } t;
t.s.a[0] = 1; // OK
t.s.a[1] = 1; // error
volatile struct T *pt = &t;
pt->s.a[0] = 1; // OK
pt->s.a[1] = 1; // error
}
__attribute__ ((noinline, noclone))
void
fn5 (void)
{
volatile struct S { char b; char a[1]; } s;
volatile struct U { int a; struct S s; } u;
u.s.a[0] = 1; // OK
u.s.a[1] = 1; // error
volatile struct U *pu = &u;
pu->s.a[0] = 1; // OK
pu->s.a[1] = 1; // error in strict mode
}
int
main (void)
{
fn1 ();
fn2 ();
fn3 ();
fn4 ();
fn5 ();
return 0;
}
/* { dg-output "index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */

View File

@ -0,0 +1,37 @@
/* { dg-do compile } */
/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
/* Test off-by-one. */
struct S { int a; int b; } s[4], *t;
struct U { int a[10]; } u[4], *v;
volatile int *a, *b, *c;
volatile void *d;
volatile int e[4][4];
int
main (void)
{
t = &s[4]; // OK
a = &s[4].a; // Error
b = &s[4].b; // Error
d = &e[4]; // OK
c = &e[4][0]; // Error
c = &e[3][4]; // OK
c = &e[3][3]; // OK
a = &u[4].a[9]; // Error
a = &u[4].a[10]; // Error
a = &u[3].a[9]; // OK
a = &u[3].a[10]; // OK
a = &u[3].a[11]; // Error
return 0;
}
/* { dg-output "index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'int \\\[4\\\]\\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */

View File

@ -665,6 +665,13 @@ enum annot_expr_kind {
annot_expr_kind_last
};
/* Internal functions. */
enum internal_fn {
#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
#include "internal-fn.def"
#undef DEF_INTERNAL_FN
IFN_LAST
};
/*---------------------------------------------------------------------------
Type definitions
@ -787,6 +794,9 @@ struct GTY(()) tree_base {
/* SSA version number. This field is only used with SSA_NAME. */
unsigned int version;
/* Internal function code. */
enum internal_fn ifn;
} GTY((skip(""))) u;
};

View File

@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#include "value-prof.h"
#include "predict.h"
#include "wide-int-print.h"
#include "internal-fn.h"
#include <new> // For placement-new.
@ -1753,7 +1754,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
break;
case CALL_EXPR:
print_call_name (buffer, CALL_EXPR_FN (node), flags);
if (CALL_EXPR_FN (node) != NULL_TREE)
print_call_name (buffer, CALL_EXPR_FN (node), flags);
else
pp_string (buffer, internal_fn_name (CALL_EXPR_IFN (node)));
/* Print parameters. */
pp_space (buffer);

View File

@ -8971,6 +8971,10 @@ get_callee_fndecl (const_tree call)
called. */
addr = CALL_EXPR_FN (call);
/* If there is no function, return early. */
if (addr == NULL_TREE)
return NULL_TREE;
STRIP_NOPS (addr);
/* If this is a readonly function pointer, extract its initial value. */
@ -10598,6 +10602,27 @@ build_call_expr (tree fndecl, int n, ...)
return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray);
}
/* Build internal call expression. This is just like CALL_EXPR, except
its CALL_EXPR_FN is NULL. It will get gimplified later into ordinary
internal function. */
tree
build_call_expr_internal_loc (location_t loc, enum internal_fn ifn,
tree type, int n, ...)
{
va_list ap;
int i;
tree fn = build_call_1 (type, NULL_TREE, n);
va_start (ap, n);
for (i = 0; i < n; i++)
CALL_EXPR_ARG (fn, i) = va_arg (ap, tree);
va_end (ap);
SET_EXPR_LOCATION (fn, loc);
CALL_EXPR_IFN (fn) = ifn;
return fn;
}
/* Create a new constant string literal and return a char* pointer to it.
The STRING_CST value is the LEN characters at STR. */
tree

View File

@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3)
/* Function call. CALL_EXPRs are represented by variably-sized expression
nodes. There are at least three fixed operands. Operand 0 is an
INTEGER_CST node containing the total operand count, the number of
arguments plus 3. Operand 1 is the function, while operand 2 is
arguments plus 3. Operand 1 is the function or NULL, while operand 2 is
is static chain argument, or NULL. The remaining operands are the
arguments to the call. */
DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3)

View File

@ -1134,12 +1134,12 @@ extern void protected_set_expr_location (tree, location_t);
#define ASSERT_EXPR_VAR(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0)
#define ASSERT_EXPR_COND(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1)
/* CALL_EXPR accessors.
*/
/* CALL_EXPR accessors. */
#define CALL_EXPR_FN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 1)
#define CALL_EXPR_STATIC_CHAIN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 2)
#define CALL_EXPR_ARG(NODE, I) TREE_OPERAND (CALL_EXPR_CHECK (NODE), (I) + 3)
#define call_expr_nargs(NODE) (VL_EXP_OPERAND_LENGTH (NODE) - 3)
#define CALL_EXPR_IFN(NODE) (CALL_EXPR_CHECK (NODE)->base.u.ifn)
/* CALL_EXPR_ARGP returns a pointer to the argument vector for NODE.
We can't use &CALL_EXPR_ARG (NODE, 0) because that will complain if
@ -3631,6 +3631,8 @@ extern tree build_call_expr_loc_array (location_t, tree, int, tree *);
extern tree build_call_expr_loc_vec (location_t, tree, vec<tree, va_gc> *);
extern tree build_call_expr_loc (location_t, tree, int, ...);
extern tree build_call_expr (tree, int, ...);
extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
tree, int, ...);
extern tree build_string_literal (int, const char *);
/* Construct various nodes representing data types. */

View File

@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type)
gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
if (TREE_CODE (type) == REAL_TYPE)
return tree_to_uhwi (TYPE_SIZE (type));
else
else if (INTEGRAL_TYPE_P (type))
{
int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
gcc_assert (prec != -1);
return (prec << 1) | !TYPE_UNSIGNED (type);
}
else
return 0;
}
/* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
descriptor. It first looks into the hash table; if not found,
create the VAR_DECL, put it into the hash table and return the
ADDR_EXPR of it. TYPE describes a particular type. WANT_POINTER_TYPE_P
means whether we are interested in the pointer type and not the pointer
itself. */
ADDR_EXPR of it. TYPE describes a particular type. PSTYLE is
an enum controlling how we want to print the type. */
tree
ubsan_type_descriptor (tree type, bool want_pointer_type_p)
ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
{
/* See through any typedefs. */
type = TYPE_MAIN_VARIANT (type);
@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
unsigned short tkind, tinfo;
/* Get the name of the type, or the name of the pointer type. */
if (want_pointer_type_p)
if (pstyle == UBSAN_PRINT_POINTER)
{
gcc_assert (POINTER_TYPE_P (type));
type2 = TREE_TYPE (type);
@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
/* If an array, get its type. */
type2 = strip_array_types (type2);
if (pstyle == UBSAN_PRINT_ARRAY)
{
while (POINTER_TYPE_P (type2))
deref_depth++, type2 = TREE_TYPE (type2);
}
if (TYPE_NAME (type2) != NULL)
{
if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
/* Decorate the type name with '', '*', "struct", or "union". */
pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
if (want_pointer_type_p)
if (pstyle == UBSAN_PRINT_POINTER)
{
int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
TYPE_VOLATILE (type2) ? "volatile " : "",
@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
pretty_name[pos++] = '\'';
pretty_name[pos] = '\0';
}
else if (pstyle == UBSAN_PRINT_ARRAY)
{
/* Pretty print the array dimensions. */
gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
tree t = type;
int pos = sprintf (pretty_name, "'%s ", tname);
while (deref_depth-- > 0)
pretty_name[pos++] = '*';
while (TREE_CODE (t) == ARRAY_TYPE)
{
pretty_name[pos++] = '[';
tree dom = TYPE_DOMAIN (t);
if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
else
/* ??? We can't determine the variable name; print VLA unspec. */
pretty_name[pos++] = '*';
pretty_name[pos++] = ']';
t = TREE_TYPE (t);
}
pretty_name[pos++] = '\'';
pretty_name[pos] = '\0';
/* Save the tree with stripped types. */
type = t;
}
else
sprintf (pretty_name, "'%s'", tname);
@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t)
"__builtin___ubsan_", 18) == 0;
}
/* Expand the UBSAN_BOUNDS special builtin function. */
void
ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
location_t loc = gimple_location (stmt);
gcc_assert (gimple_call_num_args (stmt) == 3);
/* Pick up the arguments of the UBSAN_BOUNDS call. */
tree type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 0)));
tree index = gimple_call_arg (stmt, 1);
tree orig_index_type = TREE_TYPE (index);
tree bound = gimple_call_arg (stmt, 2);
gimple_stmt_iterator gsi_orig = *gsi;
/* Create condition "if (index > bound)". */
basic_block then_bb, fallthru_bb;
gimple_stmt_iterator cond_insert_point
= create_cond_insert_point (gsi, 0/*before_p*/, false, true,
&then_bb, &fallthru_bb);
index = fold_convert (TREE_TYPE (bound), index);
index = force_gimple_operand_gsi (&cond_insert_point, index,
true/*simple_p*/, NULL_TREE,
false/*before*/, GSI_NEW_STMT);
gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
gimple_set_location (g, loc);
gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
/* Generate __ubsan_handle_out_of_bounds call. */
*gsi = gsi_after_labels (then_bb);
if (flag_sanitize_undefined_trap_on_error)
g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
else
{
tree data
= ubsan_create_data ("__ubsan_out_of_bounds_data", &loc, NULL,
ubsan_type_descriptor (type, UBSAN_PRINT_ARRAY),
ubsan_type_descriptor (orig_index_type),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode
= flag_sanitize_recover
? BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS
: BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT;
tree fn = builtin_decl_explicit (bcode);
tree val = force_gimple_operand_gsi (gsi, ubsan_encode_value (index),
true, NULL_TREE, true,
GSI_SAME_STMT);
g = gimple_build_call (fn, 2, data, val);
}
gimple_set_location (g, loc);
gsi_insert_before (gsi, g, GSI_SAME_STMT);
/* Get rid of the UBSAN_BOUNDS call from the IR. */
unlink_stmt_vdef (stmt);
gsi_remove (&gsi_orig, true);
/* Point GSI to next logical statement. */
*gsi = gsi_start_bb (fallthru_bb);
}
/* Expand UBSAN_NULL internal call. */
void
@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
tree fn = builtin_decl_implicit (bcode);
const struct ubsan_mismatch_data m
= { build_zero_cst (pointer_sized_int_node), ckind };
tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m,
ubsan_type_descriptor (TREE_TYPE (ptr),
true), NULL_TREE);
tree data
= ubsan_create_data ("__ubsan_null_data", &loc, &m,
ubsan_type_descriptor (TREE_TYPE (ptr),
UBSAN_PRINT_POINTER),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
g = gimple_build_call (fn, 2, data,
build_zero_cst (pointer_sized_int_node));
@ -689,8 +788,7 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
ubsan_type_descriptor (lhstype, false),
NULL_TREE);
ubsan_type_descriptor (lhstype), NULL_TREE);
enum built_in_function fn_code;
switch (code)
@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
else
{
tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL,
ubsan_type_descriptor (type, false),
NULL_TREE);
ubsan_type_descriptor (type), NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode
= flag_sanitize_recover
@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
{
/* Create the __ubsan_handle_float_cast_overflow fn call. */
tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
NULL,
ubsan_type_descriptor (expr_type, false),
ubsan_type_descriptor (type, false),
NULL_TREE);
NULL, ubsan_type_descriptor (expr_type),
ubsan_type_descriptor (type), NULL_TREE);
enum built_in_function bcode
= flag_sanitize_recover
? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW

View File

@ -30,17 +30,25 @@ enum ubsan_null_ckind {
UBSAN_MEMBER_CALL
};
/* This controls how ubsan prints types. Used in ubsan_type_descriptor. */
enum ubsan_print_style {
UBSAN_PRINT_NORMAL,
UBSAN_PRINT_POINTER,
UBSAN_PRINT_ARRAY
};
/* An extra data used by ubsan pointer checking. */
struct ubsan_mismatch_data {
tree align;
tree ckind;
};
extern void ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
extern tree ubsan_instrument_unreachable (location_t);
extern tree ubsan_create_data (const char *, const location_t *,
const struct ubsan_mismatch_data *, ...);
extern tree ubsan_type_descriptor (tree, bool);
extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);
extern tree ubsan_encode_value (tree, bool = false);
extern bool is_ubsan_builtin_p (tree);
extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);