aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family
diff options
context:
space:
mode:
authorMarek Polacek <mpolacek@gcc.gnu.org>2014-06-20 21:20:51 +0000
committerMarek Polacek <mpolacek@gcc.gnu.org>2014-06-20 21:20:51 +0000
commit0e37a2f33da9dcc33921ded88f700c28dff7960e (patch)
tree909724d7e8bcbb98c38c8f42a5f641e17c4ac7ee /gcc/c-family
parent87681fb55051518fb40426e61ee4fbc2a47073c3 (diff)
downloadgcc-0e37a2f33da9dcc33921ded88f700c28dff7960e.zip
gcc-0e37a2f33da9dcc33921ded88f700c28dff7960e.tar.gz
gcc-0e37a2f33da9dcc33921ded88f700c28dff7960e.tar.bz2
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
Diffstat (limited to 'gcc/c-family')
-rw-r--r--gcc/c-family/ChangeLog19
-rw-r--r--gcc/c-family/c-gimplify.c53
-rw-r--r--gcc/c-family/c-ubsan.c142
-rw-r--r--gcc/c-family/c-ubsan.h3
4 files changed, 201 insertions, 16 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index d195db7..e37aa46d 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -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.
diff --git a/gcc/c-family/c-gimplify.c b/gcc/c-family/c-gimplify.c
index 737be4d..c797d99 100644
--- a/gcc/c-family/c-gimplify.c
+++ b/gcc/c-family/c-gimplify.c
@@ -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:
diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c
index 8d5b685..3698580 100644
--- a/gcc/c-family/c-ubsan.c
+++ b/gcc/c-family/c-ubsan.c
@@ -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;
+ }
+ }
+}
diff --git a/gcc/c-family/c-ubsan.h b/gcc/c-family/c-ubsan.h
index e504b90..edf5bc6 100644
--- a/gcc/c-family/c-ubsan.h
+++ b/gcc/c-family/c-ubsan.h
@@ -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 */