aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog38
-rw-r--r--gcc/asan.c7
-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
-rw-r--r--gcc/doc/invoke.texi7
-rw-r--r--gcc/flag-types.h4
-rw-r--r--gcc/gimplify.c18
-rw-r--r--gcc/internal-fn.c10
-rw-r--r--gcc/internal-fn.def1
-rw-r--r--gcc/internal-fn.h7
-rw-r--r--gcc/opts.c1
-rw-r--r--gcc/sanitizer.def8
-rw-r--r--gcc/testsuite/ChangeLog9
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/bounds-1.c75
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/bounds-2.c168
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/bounds-3.c23
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/bounds-4.c17
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/bounds-5.c88
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/bounds-6.c37
-rw-r--r--gcc/tree-core.h10
-rw-r--r--gcc/tree-pretty-print.c6
-rw-r--r--gcc/tree.c25
-rw-r--r--gcc/tree.def2
-rw-r--r--gcc/tree.h6
-rw-r--r--gcc/ubsan.c131
-rw-r--r--gcc/ubsan.h10
28 files changed, 877 insertions, 48 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7d5a064..43b9939 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/asan.c b/gcc/asan.c
index 9e696a4..eee1a71 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -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;
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 */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f7a54fa..0d4bd00 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index ed00046..4b8e338 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -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
};
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 3dcb4af..338c5c0 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -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
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 68b2b66..78f59d6 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -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 -. */
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 31dc4c9..f0766bc 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -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)
diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
index 9c3215f..2dcf44e 100644
--- a/gcc/internal-fn.h
+++ b/gcc/internal-fn.h
@@ -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. */
diff --git a/gcc/opts.c b/gcc/opts.c
index 324d545..3ab06c6 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -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;
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index b4af164..1f5ef21 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -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)
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 98d6379..dcab923 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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.
diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-1.c b/gcc/testsuite/c-c++-common/ubsan/bounds-1.c
new file mode 100644
index 0000000..aa192d3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/bounds-1.c
@@ -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;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-2.c b/gcc/testsuite/c-c++-common/ubsan/bounds-2.c
new file mode 100644
index 0000000..95f77c2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/bounds-2.c
@@ -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)" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-3.c b/gcc/testsuite/c-c++-common/ubsan/bounds-3.c
new file mode 100644
index 0000000..fcf71a3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/bounds-3.c
@@ -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]);
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-4.c b/gcc/testsuite/c-c++-common/ubsan/bounds-4.c
new file mode 100644
index 0000000..7748780
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/bounds-4.c
@@ -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;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-5.c b/gcc/testsuite/c-c++-common/ubsan/bounds-5.c
new file mode 100644
index 0000000..7b7d76d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/bounds-5.c
@@ -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)" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-6.c b/gcc/testsuite/c-c++-common/ubsan/bounds-6.c
new file mode 100644
index 0000000..78ad29a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/bounds-6.c
@@ -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)" } */
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 52d93ec..a2c88f9 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -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;
};
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index 59a825c..8522d79 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -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);
diff --git a/gcc/tree.c b/gcc/tree.c
index 240fa92..ac4012c 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -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
diff --git a/gcc/tree.def b/gcc/tree.def
index 464c8c7..299aacc 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -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)
diff --git a/gcc/tree.h b/gcc/tree.h
index a5dbc5f6..41de5c1 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -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. */
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 5a8a447..5e1c3e7 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -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
diff --git a/gcc/ubsan.h b/gcc/ubsan.h
index b008419..485449c 100644
--- a/gcc/ubsan.h
+++ b/gcc/ubsan.h
@@ -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);