Commit 0e37a2f3 by Marek Polacek

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
parent 87681fb5
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> 2014-06-20 Maciej W. Rozycki <macro@codesourcery.com>
* config/rs6000/rs6000.md: Append `DONE' to preparation * config/rs6000/rs6000.md: Append `DONE' to preparation
......
...@@ -2762,6 +2762,9 @@ pass_sanopt::execute (function *fun) ...@@ -2762,6 +2762,9 @@ pass_sanopt::execute (function *fun)
case IFN_UBSAN_NULL: case IFN_UBSAN_NULL:
ubsan_expand_null_ifn (gsi); ubsan_expand_null_ifn (gsi);
break; break;
case IFN_UBSAN_BOUNDS:
ubsan_expand_bounds_ifn (&gsi);
break;
default: default:
break; break;
} }
...@@ -2772,6 +2775,10 @@ pass_sanopt::execute (function *fun) ...@@ -2772,6 +2775,10 @@ pass_sanopt::execute (function *fun)
print_gimple_stmt (dump_file, stmt, 0, dump_flags); print_gimple_stmt (dump_file, stmt, 0, dump_flags);
fprintf (dump_file, "\n"); 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; return 0;
......
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> 2014-06-20 Hale Wang <hale.wang@arm.com>
PR lto/61123 PR lto/61123
......
...@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3. If not see ...@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3. If not see
#include "c-pretty-print.h" #include "c-pretty-print.h"
#include "cgraph.h" #include "cgraph.h"
#include "cilk.h" #include "cilk.h"
#include "c-ubsan.h"
#include "pointer-set.h"
/* The gimplification pass converts the language-dependent trees /* The gimplification pass converts the language-dependent trees
(ld-trees) emitted by the parser into language-independent 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 ...@@ -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 walk back up, we check that they fit our constraints, and copy them
into temporaries if not. */ 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. */ /* Gimplification of statement trees. */
/* Convert the tree representation of FNDECL from C frontend trees to /* Convert the tree representation of FNDECL from C frontend trees to
...@@ -79,6 +114,14 @@ c_genericize (tree fndecl) ...@@ -79,6 +114,14 @@ c_genericize (tree fndecl)
int local_dump_flags; int local_dump_flags;
struct cgraph_node *cgn; 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 the C-specific tree IR. */
dump_orig = dump_begin (TDI_original, &local_dump_flags); dump_orig = dump_begin (TDI_original, &local_dump_flags);
if (dump_orig) if (dump_orig)
......
...@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/c-common.h" #include "c-family/c-common.h"
#include "c-family/c-ubsan.h" #include "c-family/c-ubsan.h"
#include "asan.h" #include "asan.h"
#include "internal-fn.h"
/* Instrument division by zero and INT_MIN / -1. If not instrumenting, /* Instrument division by zero and INT_MIN / -1. If not instrumenting,
return NULL_TREE. */ return NULL_TREE. */
...@@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1) ...@@ -77,15 +78,27 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
return NULL_TREE; return NULL_TREE;
/* In case we have a SAVE_EXPR in a conditional context, we need to /* 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 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
if (flag_sanitize_undefined_trap_on_error) if (flag_sanitize_undefined_trap_on_error)
tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
else else
{ {
tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL, tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
ubsan_type_descriptor (type, false), ubsan_type_descriptor (type), NULL_TREE);
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data); data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode enum built_in_function bcode
= flag_sanitize_recover = flag_sanitize_recover
...@@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, ...@@ -154,7 +167,20 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
return NULL_TREE; return NULL_TREE;
/* In case we have a SAVE_EXPR in a conditional context, we need to /* 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 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t,
tt ? tt : integer_zero_node); tt ? tt : integer_zero_node);
...@@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, ...@@ -164,10 +190,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
else else
{ {
tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL, tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL,
ubsan_type_descriptor (type0, false), ubsan_type_descriptor (type0),
ubsan_type_descriptor (type1, false), ubsan_type_descriptor (type1), NULL_TREE);
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data); data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode enum built_in_function bcode
...@@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size) ...@@ -197,8 +221,7 @@ ubsan_instrument_vla (location_t loc, tree size)
else else
{ {
tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL, tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL,
ubsan_type_descriptor (type, false), ubsan_type_descriptor (type), NULL_TREE);
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data); data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode enum built_in_function bcode
= flag_sanitize_recover = flag_sanitize_recover
...@@ -228,3 +251,102 @@ ubsan_instrument_return (location_t loc) ...@@ -228,3 +251,102 @@ ubsan_instrument_return (location_t loc)
tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN); 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)); 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;
}
}
}
...@@ -25,5 +25,8 @@ extern tree ubsan_instrument_division (location_t, tree, tree); ...@@ -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_shift (location_t, enum tree_code, tree, tree);
extern tree ubsan_instrument_vla (location_t, tree); extern tree ubsan_instrument_vla (location_t, tree);
extern tree ubsan_instrument_return (location_t); 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 */ #endif /* GCC_C_UBSAN_H */
...@@ -5448,6 +5448,13 @@ This option enables floating-point type to integer conversion checking. ...@@ -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. We check that the result of the conversion does not overflow.
This option does not work well with @code{FE_INVALID} exceptions enabled. 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 @item -fsanitize-recover
@opindex fsanitize-recover @opindex fsanitize-recover
By default @option{-fsanitize=undefined} sanitization (and its suboptions By default @option{-fsanitize=undefined} sanitization (and its suboptions
......
...@@ -230,9 +230,11 @@ enum sanitize_code { ...@@ -230,9 +230,11 @@ enum sanitize_code {
SANITIZE_ENUM = 1 << 11, SANITIZE_ENUM = 1 << 11,
SANITIZE_FLOAT_DIVIDE = 1 << 12, SANITIZE_FLOAT_DIVIDE = 1 << 12,
SANITIZE_FLOAT_CAST = 1 << 13, SANITIZE_FLOAT_CAST = 1 << 13,
SANITIZE_BOUNDS = 1 << 14,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN | 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 SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
}; };
......
...@@ -2264,6 +2264,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value) ...@@ -2264,6 +2264,24 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
if (! EXPR_HAS_LOCATION (*expr_p)) if (! EXPR_HAS_LOCATION (*expr_p))
SET_EXPR_LOCATION (*expr_p, input_location); 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. /* This may be a call to a builtin function.
Builtin function calls may be transformed into different Builtin function calls may be transformed into different
......
...@@ -20,8 +20,8 @@ along with GCC; see the file COPYING3. If not see ...@@ -20,8 +20,8 @@ along with GCC; see the file COPYING3. If not see
#include "config.h" #include "config.h"
#include "system.h" #include "system.h"
#include "coretypes.h" #include "coretypes.h"
#include "internal-fn.h"
#include "tree.h" #include "tree.h"
#include "internal-fn.h"
#include "stor-layout.h" #include "stor-layout.h"
#include "expr.h" #include "expr.h"
#include "optabs.h" #include "optabs.h"
...@@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED) ...@@ -159,6 +159,14 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
gcc_unreachable (); 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. /* Add sub/add overflow checking to the statement STMT.
CODE says whether the operation is +, or -. */ CODE says whether the operation is +, or -. */
......
...@@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF) ...@@ -48,6 +48,7 @@ DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF)
DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF) DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF)
DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_NULL, 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_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_CHECK_SUB, 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) DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
......
...@@ -20,13 +20,6 @@ along with GCC; see the file COPYING3. If not see ...@@ -20,13 +20,6 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_INTERNAL_FN_H #ifndef GCC_INTERNAL_FN_H
#define 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 /* Return the name of internal function FN. The name is only meaningful
for dumps; it has no linkage. */ for dumps; it has no linkage. */
......
...@@ -1467,6 +1467,7 @@ common_handle_option (struct gcc_options *opts, ...@@ -1467,6 +1467,7 @@ common_handle_option (struct gcc_options *opts,
sizeof "float-divide-by-zero" - 1 }, sizeof "float-divide-by-zero" - 1 },
{ "float-cast-overflow", SANITIZE_FLOAT_CAST, { "float-cast-overflow", SANITIZE_FLOAT_CAST,
sizeof "float-cast-overflow" - 1 }, sizeof "float-cast-overflow" - 1 },
{ "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
{ NULL, 0, 0 } { NULL, 0, 0 }
}; };
const char *comma; const char *comma;
......
...@@ -409,3 +409,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT, ...@@ -409,3 +409,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
"__ubsan_handle_float_cast_overflow_abort", "__ubsan_handle_float_cast_overflow_abort",
BT_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR,
ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) 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)
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> 2014-06-20 Yufeng Zhang <yufeng.zhang@arm.com>
Make the tests big-endian friendly. Make the tests big-endian friendly.
......
/* { 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;
}
/* { 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)" } */
/* { 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]);
}
/* { 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;
}
/* { 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)" } */
/* { 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)" } */
...@@ -665,6 +665,13 @@ enum annot_expr_kind { ...@@ -665,6 +665,13 @@ enum annot_expr_kind {
annot_expr_kind_last 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 Type definitions
...@@ -787,6 +794,9 @@ struct GTY(()) tree_base { ...@@ -787,6 +794,9 @@ struct GTY(()) tree_base {
/* SSA version number. This field is only used with SSA_NAME. */ /* SSA version number. This field is only used with SSA_NAME. */
unsigned int version; unsigned int version;
/* Internal function code. */
enum internal_fn ifn;
} GTY((skip(""))) u; } GTY((skip(""))) u;
}; };
......
...@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#include "value-prof.h" #include "value-prof.h"
#include "predict.h" #include "predict.h"
#include "wide-int-print.h" #include "wide-int-print.h"
#include "internal-fn.h"
#include <new> // For placement-new. #include <new> // For placement-new.
...@@ -1753,7 +1754,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, ...@@ -1753,7 +1754,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
break; break;
case CALL_EXPR: case CALL_EXPR:
if (CALL_EXPR_FN (node) != NULL_TREE)
print_call_name (buffer, CALL_EXPR_FN (node), flags); print_call_name (buffer, CALL_EXPR_FN (node), flags);
else
pp_string (buffer, internal_fn_name (CALL_EXPR_IFN (node)));
/* Print parameters. */ /* Print parameters. */
pp_space (buffer); pp_space (buffer);
......
...@@ -8971,6 +8971,10 @@ get_callee_fndecl (const_tree call) ...@@ -8971,6 +8971,10 @@ get_callee_fndecl (const_tree call)
called. */ called. */
addr = CALL_EXPR_FN (call); addr = CALL_EXPR_FN (call);
/* If there is no function, return early. */
if (addr == NULL_TREE)
return NULL_TREE;
STRIP_NOPS (addr); STRIP_NOPS (addr);
/* If this is a readonly function pointer, extract its initial value. */ /* If this is a readonly function pointer, extract its initial value. */
...@@ -10598,6 +10602,27 @@ build_call_expr (tree fndecl, int n, ...) ...@@ -10598,6 +10602,27 @@ build_call_expr (tree fndecl, int n, ...)
return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray); 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. /* Create a new constant string literal and return a char* pointer to it.
The STRING_CST value is the LEN characters at STR. */ The STRING_CST value is the LEN characters at STR. */
tree tree
......
...@@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3) ...@@ -574,7 +574,7 @@ DEFTREECODE (BIND_EXPR, "bind_expr", tcc_expression, 3)
/* Function call. CALL_EXPRs are represented by variably-sized expression /* Function call. CALL_EXPRs are represented by variably-sized expression
nodes. There are at least three fixed operands. Operand 0 is an nodes. There are at least three fixed operands. Operand 0 is an
INTEGER_CST node containing the total operand count, the number of 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 is static chain argument, or NULL. The remaining operands are the
arguments to the call. */ arguments to the call. */
DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3) DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3)
......
...@@ -1134,12 +1134,12 @@ extern void protected_set_expr_location (tree, location_t); ...@@ -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_VAR(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0)
#define ASSERT_EXPR_COND(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1) #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_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_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_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_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. /* 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 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 *); ...@@ -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_vec (location_t, tree, vec<tree, va_gc> *);
extern tree build_call_expr_loc (location_t, tree, int, ...); extern tree build_call_expr_loc (location_t, tree, int, ...);
extern tree build_call_expr (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 *); extern tree build_string_literal (int, const char *);
/* Construct various nodes representing data types. */ /* Construct various nodes representing data types. */
......
...@@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type) ...@@ -271,23 +271,24 @@ get_ubsan_type_info_for_type (tree type)
gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type))); gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
if (TREE_CODE (type) == REAL_TYPE) if (TREE_CODE (type) == REAL_TYPE)
return tree_to_uhwi (TYPE_SIZE (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))); int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
gcc_assert (prec != -1); gcc_assert (prec != -1);
return (prec << 1) | !TYPE_UNSIGNED (type); return (prec << 1) | !TYPE_UNSIGNED (type);
} }
else
return 0;
} }
/* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
descriptor. It first looks into the hash table; if not found, descriptor. It first looks into the hash table; if not found,
create the VAR_DECL, put it into the hash table and return the 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 ADDR_EXPR of it. TYPE describes a particular type. PSTYLE is
means whether we are interested in the pointer type and not the pointer an enum controlling how we want to print the type. */
itself. */
tree 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. */ /* See through any typedefs. */
type = TYPE_MAIN_VARIANT (type); type = TYPE_MAIN_VARIANT (type);
...@@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) ...@@ -308,7 +309,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
unsigned short tkind, tinfo; unsigned short tkind, tinfo;
/* Get the name of the type, or the name of the pointer type. */ /* 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)); gcc_assert (POINTER_TYPE_P (type));
type2 = TREE_TYPE (type); type2 = TREE_TYPE (type);
...@@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) ...@@ -324,6 +325,12 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
/* If an array, get its type. */ /* If an array, get its type. */
type2 = strip_array_types (type2); 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 (TYPE_NAME (type2) != NULL)
{ {
if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE) if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
...@@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) ...@@ -338,7 +345,7 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
/* Decorate the type name with '', '*', "struct", or "union". */ /* Decorate the type name with '', '*', "struct", or "union". */
pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth); 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", int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
TYPE_VOLATILE (type2) ? "volatile " : "", TYPE_VOLATILE (type2) ? "volatile " : "",
...@@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) ...@@ -355,6 +362,33 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
pretty_name[pos++] = '\''; pretty_name[pos++] = '\'';
pretty_name[pos] = '\0'; 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 else
sprintf (pretty_name, "'%s'", tname); sprintf (pretty_name, "'%s'", tname);
...@@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t) ...@@ -550,6 +584,69 @@ is_ubsan_builtin_p (tree t)
"__builtin___ubsan_", 18) == 0; "__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. */ /* Expand UBSAN_NULL internal call. */
void void
...@@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi) ...@@ -609,9 +706,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
tree fn = builtin_decl_implicit (bcode); tree fn = builtin_decl_implicit (bcode);
const struct ubsan_mismatch_data m const struct ubsan_mismatch_data m
= { build_zero_cst (pointer_sized_int_node), ckind }; = { build_zero_cst (pointer_sized_int_node), ckind };
tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m, tree data
= ubsan_create_data ("__ubsan_null_data", &loc, &m,
ubsan_type_descriptor (TREE_TYPE (ptr), ubsan_type_descriptor (TREE_TYPE (ptr),
true), NULL_TREE); UBSAN_PRINT_POINTER),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data); data = build_fold_addr_expr_loc (loc, data);
g = gimple_build_call (fn, 2, data, g = gimple_build_call (fn, 2, data,
build_zero_cst (pointer_sized_int_node)); build_zero_cst (pointer_sized_int_node));
...@@ -689,8 +788,7 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype, ...@@ -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); return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL, tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
ubsan_type_descriptor (lhstype, false), ubsan_type_descriptor (lhstype), NULL_TREE);
NULL_TREE);
enum built_in_function fn_code; enum built_in_function fn_code;
switch (code) switch (code)
...@@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi) ...@@ -884,8 +982,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
else else
{ {
tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL, tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL,
ubsan_type_descriptor (type, false), ubsan_type_descriptor (type), NULL_TREE);
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data); data = build_fold_addr_expr_loc (loc, data);
enum built_in_function bcode enum built_in_function bcode
= flag_sanitize_recover = flag_sanitize_recover
...@@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr) ...@@ -1005,10 +1102,8 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
{ {
/* Create the __ubsan_handle_float_cast_overflow fn call. */ /* Create the __ubsan_handle_float_cast_overflow fn call. */
tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL, tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
NULL, NULL, ubsan_type_descriptor (expr_type),
ubsan_type_descriptor (expr_type, false), ubsan_type_descriptor (type), NULL_TREE);
ubsan_type_descriptor (type, false),
NULL_TREE);
enum built_in_function bcode enum built_in_function bcode
= flag_sanitize_recover = flag_sanitize_recover
? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
......
...@@ -30,17 +30,25 @@ enum ubsan_null_ckind { ...@@ -30,17 +30,25 @@ enum ubsan_null_ckind {
UBSAN_MEMBER_CALL 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. */ /* An extra data used by ubsan pointer checking. */
struct ubsan_mismatch_data { struct ubsan_mismatch_data {
tree align; tree align;
tree ckind; tree ckind;
}; };
extern void ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
extern void ubsan_expand_null_ifn (gimple_stmt_iterator); extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
extern tree ubsan_instrument_unreachable (location_t); extern tree ubsan_instrument_unreachable (location_t);
extern tree ubsan_create_data (const char *, const location_t *, extern tree ubsan_create_data (const char *, const location_t *,
const struct ubsan_mismatch_data *, ...); 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 tree ubsan_encode_value (tree, bool = false);
extern bool is_ubsan_builtin_p (tree); extern bool is_ubsan_builtin_p (tree);
extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree); extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);
......
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