aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog31
-rw-r--r--gcc/asan.c17
-rw-r--r--gcc/c-family/ChangeLog11
-rw-r--r--gcc/c-family/c-common.c20
-rw-r--r--gcc/c-family/c-common.h1
-rw-r--r--gcc/c-family/c-ubsan.c98
-rw-r--r--gcc/c-family/c-ubsan.h2
-rw-r--r--gcc/cp/ChangeLog7
-rw-r--r--gcc/cp/cp-gimplify.c21
-rw-r--r--gcc/doc/invoke.texi10
-rw-r--r--gcc/flag-types.h3
-rw-r--r--gcc/opts.c1
-rw-r--r--gcc/stor-layout.c21
-rw-r--r--gcc/stor-layout.h3
-rw-r--r--gcc/testsuite/ChangeLog15
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/align-1.c41
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/align-2.c56
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/align-3.c66
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/align-4.c14
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/align-5.c15
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/attrib-4.c15
-rw-r--r--gcc/testsuite/g++.dg/ubsan/align-1.C27
-rw-r--r--gcc/testsuite/g++.dg/ubsan/align-2.C45
-rw-r--r--gcc/testsuite/g++.dg/ubsan/align-3.C45
-rw-r--r--gcc/testsuite/g++.dg/ubsan/attrib-1.C27
-rw-r--r--gcc/testsuite/g++.dg/ubsan/null-1.C30
-rw-r--r--gcc/testsuite/g++.dg/ubsan/null-2.C39
-rw-r--r--gcc/ubsan.c171
-rw-r--r--gcc/ubsan.h7
29 files changed, 785 insertions, 74 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 13e0c5e..414b53b 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,34 @@
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * opts.c (common_handle_option): Handle -fsanitize=alignment.
+ * ubsan.h (enum ubsan_null_ckind): Add UBSAN_CTOR_CALL.
+ (ubsan_expand_bounds_ifn, ubsan_expand_null_ifn): Change return
+ type to bool.
+ * stor-layout.h (min_align_of_type): New prototype.
+ * asan.c (pass_sanopt::execute): Don't perform gsi_next if
+ ubsan_expand* told us not to do it. Remove the extra gsi_end_p
+ check.
+ * ubsan.c: Include builtins.h.
+ (ubsan_expand_bounds_ifn): Change return type to bool,
+ always return true.
+ (ubsan_expand_null_ifn): Change return type to bool, change
+ argument to gimple_stmt_iterator *. Handle both null and alignment
+ sanitization, take type from ckind argument's type rather than
+ first argument.
+ (instrument_member_call): Removed.
+ (instrument_mem_ref): Remove t argument, add mem and base arguments.
+ Handle both null and alignment sanitization, don't say whole
+ struct access is member access. Build 3 argument IFN_UBSAN_NULL
+ call instead of 2 argument.
+ (instrument_null): Adjust instrument_mem_ref caller. Don't
+ instrument calls here.
+ (pass_ubsan::gate, pass_ubsan::execute): Handle SANITIZE_ALIGNMENT
+ like SANITIZE_NULL.
+ * stor-layout.c (min_align_of_type): New function.
+ * flag-types.h (enum sanitize_code): Add SANITIZE_ALIGNMENT.
+ Or it into SANITIZE_UNDEFINED.
+ * doc/invoke.texi (-fsanitize=alignment): Document.
+
2014-07-31 Andi Kleen <ak@linux.intel.com>
* tree-ssa-tail-merge.c (same_succ_hash): Convert to inchash.
diff --git a/gcc/asan.c b/gcc/asan.c
index 4f882b5..76f21bd 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -2750,21 +2750,25 @@ pass_sanopt::execute (function *fun)
FOR_EACH_BB_FN (bb, fun)
{
gimple_stmt_iterator gsi;
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
{
gimple stmt = gsi_stmt (gsi);
+ bool no_next = false;
if (!is_gimple_call (stmt))
- continue;
+ {
+ gsi_next (&gsi);
+ continue;
+ }
if (gimple_call_internal_p (stmt))
switch (gimple_call_internal_fn (stmt))
{
case IFN_UBSAN_NULL:
- ubsan_expand_null_ifn (gsi);
+ no_next = ubsan_expand_null_ifn (&gsi);
break;
case IFN_UBSAN_BOUNDS:
- ubsan_expand_bounds_ifn (&gsi);
+ no_next = ubsan_expand_bounds_ifn (&gsi);
break;
default:
break;
@@ -2777,9 +2781,8 @@ pass_sanopt::execute (function *fun)
fprintf (dump_file, "\n");
}
- /* ubsan_expand_bounds_ifn might move us to the end of the BB. */
- if (gsi_end_p (gsi))
- break;
+ if (!no_next)
+ gsi_next (&gsi);
}
}
return 0;
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index bf1ad5b..55e4a66 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,14 @@
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * c-common.h (min_align_of_type): Removed prototype.
+ * c-common.c (min_align_of_type): Removed.
+ * c-ubsan.h (ubsan_maybe_instrument_reference,
+ ubsan_maybe_instrument_member_call): New prototypes.
+ * c-ubsan.c: Include stor-layout.h and builtins.h.
+ (ubsan_maybe_instrument_reference_or_call,
+ ubsan_maybe_instrument_reference, ubsan_maybe_instrument_call): New
+ functions.
+
2014-07-31 Marc Glisse <marc.glisse@inria.fr>
PR c++/60517
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 79d0f2f..b2a053e 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -4965,26 +4965,6 @@ c_common_get_alias_set (tree t)
return -1;
}
-/* Return the least alignment required for type TYPE. */
-
-unsigned int
-min_align_of_type (tree type)
-{
- unsigned int align = TYPE_ALIGN (type);
- align = MIN (align, BIGGEST_ALIGNMENT);
-#ifdef BIGGEST_FIELD_ALIGNMENT
- align = MIN (align, BIGGEST_FIELD_ALIGNMENT);
-#endif
- unsigned int field_align = align;
-#ifdef ADJUST_FIELD_ALIGN
- tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
- type);
- field_align = ADJUST_FIELD_ALIGN (field, field_align);
-#endif
- align = MIN (align, field_align);
- return align / BITS_PER_UNIT;
-}
-
/* Compute the value of 'sizeof (TYPE)' or '__alignof__ (TYPE)', where
the IS_SIZEOF parameter indicates which operator is being applied.
The COMPLAIN flag controls whether we should diagnose possibly
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 3e8c8e7..26aaee2 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -762,7 +762,6 @@ extern tree c_wrap_maybe_const (tree, bool);
extern tree c_save_expr (tree);
extern tree c_common_truthvalue_conversion (location_t, tree);
extern void c_apply_type_quals_to_decl (int, tree);
-extern unsigned int min_align_of_type (tree);
extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int);
extern tree c_alignof_expr (location_t, tree);
/* Print an error message for invalid operands to arith operation CODE.
diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c
index ad5dd0b..e048c53 100644
--- a/gcc/c-family/c-ubsan.c
+++ b/gcc/c-family/c-ubsan.c
@@ -31,6 +31,8 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/c-ubsan.h"
#include "asan.h"
#include "internal-fn.h"
+#include "stor-layout.h"
+#include "builtins.h"
/* Instrument division by zero and INT_MIN / -1. If not instrumenting,
return NULL_TREE. */
@@ -350,3 +352,99 @@ ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one)
}
}
}
+
+static tree
+ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree type,
+ enum ubsan_null_ckind ckind)
+{
+ tree orig_op = op;
+ bool instrument = false;
+ unsigned int mina = 0;
+
+ if (current_function_decl == NULL_TREE
+ || lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (current_function_decl)))
+ return NULL_TREE;
+
+ if (flag_sanitize & SANITIZE_ALIGNMENT)
+ {
+ mina = min_align_of_type (type);
+ if (mina <= 1)
+ mina = 0;
+ }
+ while ((TREE_CODE (op) == NOP_EXPR
+ || TREE_CODE (op) == NON_LVALUE_EXPR)
+ && TREE_CODE (TREE_TYPE (op)) == POINTER_TYPE)
+ op = TREE_OPERAND (op, 0);
+ if (TREE_CODE (op) == NOP_EXPR
+ && TREE_CODE (TREE_TYPE (op)) == REFERENCE_TYPE)
+ {
+ if (mina && mina > min_align_of_type (TREE_TYPE (TREE_TYPE (op))))
+ instrument = true;
+ }
+ else
+ {
+ if ((flag_sanitize & SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR)
+ {
+ bool strict_overflow_p = false;
+ /* tree_single_nonzero_warnv_p will not return true for non-weak
+ non-automatic decls with -fno-delete-null-pointer-checks,
+ which is disabled during -fsanitize=null. We don't want to
+ instrument those, just weak vars though. */
+ int save_flag_delete_null_pointer_checks
+ = flag_delete_null_pointer_checks;
+ flag_delete_null_pointer_checks = 1;
+ if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p)
+ || strict_overflow_p)
+ instrument = true;
+ flag_delete_null_pointer_checks
+ = save_flag_delete_null_pointer_checks;
+ }
+ else if (flag_sanitize & SANITIZE_NULL)
+ instrument = true;
+ if (mina && mina > get_pointer_alignment (op) / BITS_PER_UNIT)
+ instrument = true;
+ }
+ if (!instrument)
+ return NULL_TREE;
+ op = save_expr (orig_op);
+ tree kind = build_int_cst (TREE_TYPE (op), ckind);
+ tree align = build_int_cst (pointer_sized_int_node, mina);
+ tree call
+ = build_call_expr_internal_loc (loc, IFN_UBSAN_NULL, void_type_node,
+ 3, op, kind, align);
+ TREE_SIDE_EFFECTS (call) = 1;
+ return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op);
+}
+
+/* Instrument a NOP_EXPR to REFERENCE_TYPE if needed. */
+
+void
+ubsan_maybe_instrument_reference (tree stmt)
+{
+ tree op = TREE_OPERAND (stmt, 0);
+ op = ubsan_maybe_instrument_reference_or_call (EXPR_LOCATION (stmt), op,
+ TREE_TYPE (TREE_TYPE (stmt)),
+ UBSAN_REF_BINDING);
+ if (op)
+ TREE_OPERAND (stmt, 0) = op;
+}
+
+/* Instrument a CALL_EXPR to a method if needed. */
+
+void
+ubsan_maybe_instrument_member_call (tree stmt, bool is_ctor)
+{
+ if (call_expr_nargs (stmt) == 0)
+ return;
+ tree op = CALL_EXPR_ARG (stmt, 0);
+ if (op == error_mark_node
+ || !POINTER_TYPE_P (TREE_TYPE (op)))
+ return;
+ op = ubsan_maybe_instrument_reference_or_call (EXPR_LOCATION (stmt), op,
+ TREE_TYPE (TREE_TYPE (op)),
+ is_ctor ? UBSAN_CTOR_CALL
+ : UBSAN_MEMBER_CALL);
+ if (op)
+ CALL_EXPR_ARG (stmt, 0) = op;
+}
diff --git a/gcc/c-family/c-ubsan.h b/gcc/c-family/c-ubsan.h
index edf5bc6..7feec45 100644
--- a/gcc/c-family/c-ubsan.h
+++ b/gcc/c-family/c-ubsan.h
@@ -28,5 +28,7 @@ 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);
+extern void ubsan_maybe_instrument_reference (tree);
+extern void ubsan_maybe_instrument_member_call (tree, bool);
#endif /* GCC_C_UBSAN_H */
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 36d8b39..89a6a7b 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,10 @@
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * cp-gimplify.c (cp_genericize_r): For -fsanitize=null and/or
+ -fsanitize=alignment call ubsan_maybe_instrument_reference
+ for casts to REFERENCE_TYPE and ubsan_maybe_instrument_member_call
+ for calls to member functions.
+
2014-07-31 Marc Glisse <marc.glisse@inria.fr>
PR c++/60517
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index a35177b..5f5ba47 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1198,6 +1198,27 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
*stmt_p = size_one_node;
return NULL;
}
+ else if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
+ {
+ if (TREE_CODE (stmt) == NOP_EXPR
+ && TREE_CODE (TREE_TYPE (stmt)) == REFERENCE_TYPE)
+ ubsan_maybe_instrument_reference (stmt);
+ else if (TREE_CODE (stmt) == CALL_EXPR)
+ {
+ tree fn = CALL_EXPR_FN (stmt);
+ if (fn != NULL_TREE
+ && !error_operand_p (fn)
+ && POINTER_TYPE_P (TREE_TYPE (fn))
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) == METHOD_TYPE)
+ {
+ bool is_ctor
+ = TREE_CODE (fn) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
+ && DECL_CONSTRUCTOR_P (TREE_OPERAND (fn, 0));
+ ubsan_maybe_instrument_member_call (stmt, is_ctor);
+ }
+ }
+ }
pointer_set_insert (p_set, *stmt_p);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 2837096..7378a2e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -5463,7 +5463,8 @@ instead.
This option enables pointer checking. Particularly, the application
built with this option turned on will issue an error message when it
tries to dereference a NULL pointer, or if a reference (possibly an
-rvalue reference) is bound to a NULL pointer.
+rvalue reference) is bound to a NULL pointer, or if a method is invoked
+on an object pointed by a NULL pointer.
@item -fsanitize=return
@opindex fsanitize=return
@@ -5490,6 +5491,13 @@ This option enables instrumentation of array bounds. Various out of bounds
accesses are detected. Flexible array members and initializers of variables
with static storage are not instrumented.
+@item -fsanitize=alignment
+@opindex fsanitize=alignment
+
+This option enables checking of alignment of pointers when they are
+dereferenced, or when a reference is bound to insufficiently aligned target,
+or when a method or constructor is invoked on insufficiently aligned object.
+
@item -fsanitize=float-divide-by-zero
@opindex fsanitize=float-divide-by-zero
Detect floating-point division by zero. Unlike other similar options,
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index bf813b6..135c343 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -233,10 +233,11 @@ enum sanitize_code {
SANITIZE_FLOAT_DIVIDE = 1 << 14,
SANITIZE_FLOAT_CAST = 1 << 15,
SANITIZE_BOUNDS = 1 << 16,
+ SANITIZE_ALIGNMENT = 1 << 17,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
| SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
- | SANITIZE_BOUNDS,
+ | SANITIZE_BOUNDS | SANITIZE_ALIGNMENT,
SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
};
diff --git a/gcc/opts.c b/gcc/opts.c
index 4b0af82..be1867c 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1492,6 +1492,7 @@ common_handle_option (struct gcc_options *opts,
{ "float-cast-overflow", SANITIZE_FLOAT_CAST,
sizeof "float-cast-overflow" - 1 },
{ "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
+ { "alignment", SANITIZE_ALIGNMENT, sizeof "alignment" - 1 },
{ NULL, 0, 0 }
};
const char *comma;
diff --git a/gcc/stor-layout.c b/gcc/stor-layout.c
index 109264b..1c65490 100644
--- a/gcc/stor-layout.c
+++ b/gcc/stor-layout.c
@@ -2390,6 +2390,27 @@ layout_type (tree type)
gcc_assert (!TYPE_ALIAS_SET_KNOWN_P (type));
}
+/* Return the least alignment required for type TYPE. */
+
+unsigned int
+min_align_of_type (tree type)
+{
+ unsigned int align = TYPE_ALIGN (type);
+ align = MIN (align, BIGGEST_ALIGNMENT);
+#ifdef BIGGEST_FIELD_ALIGNMENT
+ align = MIN (align, BIGGEST_FIELD_ALIGNMENT);
+#endif
+ unsigned int field_align = align;
+#ifdef ADJUST_FIELD_ALIGN
+ tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
+ type);
+ field_align = ADJUST_FIELD_ALIGN (field, field_align);
+ ggc_free (field);
+#endif
+ align = MIN (align, field_align);
+ return align / BITS_PER_UNIT;
+}
+
/* Vector types need to re-check the target flags each time we report
the machine mode. We need to do this because attribute target can
change the result of vector_mode_supported_p and have_regs_of_mode
diff --git a/gcc/stor-layout.h b/gcc/stor-layout.h
index 0ff98f8..f7c5267 100644
--- a/gcc/stor-layout.h
+++ b/gcc/stor-layout.h
@@ -59,6 +59,9 @@ extern void layout_decl (tree, unsigned);
node, does nothing except for the first time. */
extern void layout_type (tree);
+/* Return the least alignment in bytes required for type TYPE. */
+extern unsigned int min_align_of_type (tree);
+
/* Construct various nodes representing fract or accum data types. */
extern tree make_fract_type (int, int, int);
extern tree make_accum_type (int, int, int);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 37b42b3..cf22371 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,18 @@
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * c-c++-common/ubsan/align-1.c: New test.
+ * c-c++-common/ubsan/align-2.c: New test.
+ * c-c++-common/ubsan/align-3.c: New test.
+ * c-c++-common/ubsan/align-4.c: New test.
+ * c-c++-common/ubsan/align-5.c: New test.
+ * c-c++-common/ubsan/attrib-4.c: New test.
+ * g++.dg/ubsan/align-1.C: New test.
+ * g++.dg/ubsan/align-2.C: New test.
+ * g++.dg/ubsan/align-3.C: New test.
+ * g++.dg/ubsan/attrib-1.C: New test.
+ * g++.dg/ubsan/null-1.C: New test.
+ * g++.dg/ubsan/null-2.C: New test.
+
2014-08-01 Tom de Vries <tom@codesourcery.com>
* lib/target-supports.exp (check_effective_target_glibc)
diff --git a/gcc/testsuite/c-c++-common/ubsan/align-1.c b/gcc/testsuite/c-c++-common/ubsan/align-1.c
new file mode 100644
index 0000000..2e40e83
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/align-1.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover" } */
+
+struct S { int a; char b; long long c; short d[10]; };
+struct T { char a; long long b; };
+struct U { char a; int b; int c; long long d; struct S e; struct T f; };
+struct V { long long a; struct S b; struct T c; struct U u; } v;
+
+__attribute__((noinline, noclone)) void
+f1 (int *p, int *q, char *r, long long *s)
+{
+ *p = *q + *r + *s;
+}
+
+
+__attribute__((noinline, noclone)) int
+f2 (struct S *p)
+{
+ return p->a;
+}
+
+__attribute__((noinline, noclone)) long long
+f3 (struct S *p, int i)
+{
+ return p->c + p->d[1] + p->d[i];
+}
+
+__attribute__((noinline, noclone)) long long
+f4 (long long *p)
+{
+ return *p;
+}
+
+int
+main ()
+{
+ f1 (&v.u.b, &v.u.c, &v.u.a, &v.u.d);
+ if (f2 (&v.u.e) + f3 (&v.u.e, 4) + f4 (&v.u.f.b) != 0)
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/align-2.c b/gcc/testsuite/c-c++-common/ubsan/align-2.c
new file mode 100644
index 0000000..071de8c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/align-2.c
@@ -0,0 +1,56 @@
+/* Limit this to known non-strict alignment targets. */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-fsanitize=alignment" } */
+
+struct S { int a; char b; long long c; short d[10]; };
+struct T { char a; long long b; };
+struct U { char a; int b; int c; long long d; struct S e; struct T f; } __attribute__((packed));
+struct V { long long a; struct S b; struct T c; struct U u; } v;
+
+__attribute__((noinline, noclone)) void
+f1 (int *p, int *q, char *r, long long *s)
+{
+ *p =
+ *q
+ + *r
+ + *s;
+}
+
+
+__attribute__((noinline, noclone)) int
+f2 (struct S *p)
+{
+ return p->a;
+}
+
+__attribute__((noinline, noclone)) long long
+f3 (struct S *p, int i)
+{
+ return p->c
+ + p->d[1]
+ + p->d[i];
+}
+
+__attribute__((noinline, noclone)) long long
+f4 (long long *p)
+{
+ return *p;
+}
+
+int
+main ()
+{
+ f1 (&v.u.b, &v.u.c, &v.u.a, &v.u.d);
+ if (f2 (&v.u.e) + f3 (&v.u.e, 4) + f4 (&v.u.f.b) != 0)
+ __builtin_abort ();
+ return 0;
+}
+
+/* { dg-output "\.c:(14|15):\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
+/* { dg-output "\.c:16:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
+/* { dg-output "\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/align-3.c b/gcc/testsuite/c-c++-common/ubsan/align-3.c
new file mode 100644
index 0000000..a509fa9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/align-3.c
@@ -0,0 +1,66 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover" } */
+
+int c;
+
+__attribute__((noinline, noclone)) void
+f1 (int *a, char *b)
+{
+ __builtin_memcpy (a, b, sizeof (*a));
+}
+
+__attribute__((noinline, noclone)) void
+f2 (int *a, char *b)
+{
+ __builtin_memcpy (b, a, sizeof (*a));
+}
+
+__attribute__((noinline, noclone)) void
+f3 (char *b)
+{
+ __builtin_memcpy (&c, b, sizeof (c));
+}
+
+__attribute__((noinline, noclone)) void
+f4 (char *b)
+{
+ __builtin_memcpy (b, &c, sizeof (c));
+}
+
+struct T
+{
+ char a;
+ short b;
+ int c;
+ long d;
+ long long e;
+ short f;
+ float g;
+ double h;
+ long double i;
+} __attribute__((packed));
+
+__attribute__((noinline, noclone)) int
+f5 (struct T *p)
+{
+ return p->a + p->b + p->c + p->d + p->e + p->f + p->g + p->h + p->i;
+}
+
+int
+main ()
+{
+ struct S { int a; char b[sizeof (int) + 1]; } s;
+ s.a = 6;
+ f2 (&s.a, &s.b[1]);
+ f1 (&s.a, &s.b[1]);
+ c = s.a + 1;
+ f4 (&s.b[1]);
+ f3 (&s.b[1]);
+ if (c != 7 || s.a != 6)
+ __builtin_abort ();
+ struct U { long long a; long double b; char c; struct T d; } u;
+ __builtin_memset (&u, 0, sizeof (u));
+ if (f5 (&u.d) != 0)
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/align-4.c b/gcc/testsuite/c-c++-common/ubsan/align-4.c
new file mode 100644
index 0000000..3252595
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/align-4.c
@@ -0,0 +1,14 @@
+/* Limit this to known non-strict alignment targets. */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-fsanitize=null,alignment" } */
+
+#include "align-2.c"
+
+/* { dg-output "\.c:(14|15):\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
+/* { dg-output "\[^\n\r]*\.c:16:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment.*" } */
+/* { dg-output "\[^\n\r]*\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
+/* { dg-output "\[^\n\r]*\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\[^\n\r]*\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\[^\n\r]*\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\[^\n\r]*\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/align-5.c b/gcc/testsuite/c-c++-common/ubsan/align-5.c
new file mode 100644
index 0000000..b94e167
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/align-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-fno-sanitize=null -fsanitize=alignment -O2" } */
+/* Check that when optimizing if we know the alignment is right
+ and we are not doing -fsanitize=null instrumentation we don't
+ instrument the alignment check. */
+
+__attribute__((noinline, noclone)) int
+foo (char *p)
+{
+ p = (char *) __builtin_assume_aligned (p, __alignof__(int));
+ int *q = (int *) p;
+ return *q;
+}
+
+/* { dg-final { scan-assembler-not "__ubsan_handle" } } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/attrib-4.c b/gcc/testsuite/c-c++-common/ubsan/attrib-4.c
new file mode 100644
index 0000000..ba0f00c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/attrib-4.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Test that we don't instrument functions marked with
+ no_sanitize_undefined attribute. */
+
+struct S { int a[16]; };
+
+__attribute__((no_sanitize_undefined)) long long
+foo (int *a, long long *b, struct S *c)
+{
+ return a[1] + *b + c->a[a[0]];
+}
+
+/* { dg-final { scan-assembler-not "__ubsan_handle" } } */
diff --git a/gcc/testsuite/g++.dg/ubsan/align-1.C b/gcc/testsuite/g++.dg/ubsan/align-1.C
new file mode 100644
index 0000000..65b1222
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/align-1.C
@@ -0,0 +1,27 @@
+// { dg-do run }
+// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
+
+typedef const long int L;
+int a = 1;
+L b = 2;
+
+int
+main (void)
+{
+ int *p = &a;
+ L *l = &b;
+
+ int &r = *p;
+ auto &r2 = *p;
+ L &lr = *l;
+
+ // Try an rvalue reference.
+ auto &&r3 = *p;
+
+ // Don't evaluate the reference initializer twice.
+ int i = 1;
+ int *q = &i;
+ int &qr = ++*q;
+ if (i != 2)
+ __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ubsan/align-2.C b/gcc/testsuite/g++.dg/ubsan/align-2.C
new file mode 100644
index 0000000..3e4f548
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/align-2.C
@@ -0,0 +1,45 @@
+// Limit this to known non-strict alignment targets.
+// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
+// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
+
+typedef const long int L;
+struct S { long int l; char buf[1 + sizeof (int) + sizeof (L)]; } s;
+struct T { char a; int b; long int c; } __attribute__((packed));
+struct U { long int a; struct T b; } u;
+
+int
+main (void)
+{
+ int *p = (int *) &s.buf[1];
+ L *l = (L *) &s.buf[1 + sizeof(int)];
+
+ int &r = *p;
+ auto &r2 = *p;
+ L &lr = *l;
+
+ // Try an rvalue reference.
+ auto &&r3 = *p;
+
+ // Don't evaluate the reference initializer twice.
+ int i = 1;
+ int *q = &i;
+ int &qr = ++*q;
+ if (i != 2)
+ __builtin_abort ();
+
+ int *s = &u.b.b;
+ L *t = &u.b.c;
+ int &r4 = *s;
+ auto &r5 = *s;
+ L &lr2 = *t;
+ auto &&r6 = *s;
+}
+
+// { dg-output "\.C:16:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:17:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:18:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'const L', which requires \[48] byte alignment.*" }
+// { dg-output "\.C:21:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:32:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:33:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:34:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'const L', which requires \[48] byte alignment.*" }
+// { dg-output "\.C:35:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment" }
diff --git a/gcc/testsuite/g++.dg/ubsan/align-3.C b/gcc/testsuite/g++.dg/ubsan/align-3.C
new file mode 100644
index 0000000..1cc40fc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/align-3.C
@@ -0,0 +1,45 @@
+// Limit this to known non-strict alignment targets.
+// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
+// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
+
+#include <new>
+
+struct U
+{
+ int a;
+ void foo () {}
+};
+struct V
+{
+ V () : a (0) {};
+ ~V () { a = 0; };
+ int a;
+ void foo () {}
+ static void bar () {}
+};
+struct S { long int l; char buf[1 + sizeof (U) + 2 * sizeof (V)]; } s;
+
+int
+main (void)
+{
+ U *p = (U *) &s.buf[1];
+ p->foo ();
+ char *q = &s.buf[1 + sizeof (U)];
+ V *u = new (q) V;
+ u->a = 1;
+ u->~V ();
+ V *v = new (&s.buf[1 + sizeof (U) + sizeof (V)]) V;
+ v->foo ();
+ v->bar (); // We don't instrument this right now.
+ v->~V ();
+}
+
+// { dg-output "\.C:26:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct U', which requires 4 byte alignment.*" }
+// { dg-output "\.C:28:\[0-9]*:\[\^\n\r]*constructor call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:14:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:29:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:30:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:15:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:31:\[0-9]*:\[\^\n\r]*constructor call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:32:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:34:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment" }
diff --git a/gcc/testsuite/g++.dg/ubsan/attrib-1.C b/gcc/testsuite/g++.dg/ubsan/attrib-1.C
new file mode 100644
index 0000000..f701d02
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/attrib-1.C
@@ -0,0 +1,27 @@
+// { dg-do compile }
+// { dg-options "-fsanitize=undefined -Wall -Wno-unused-variable -std=c++11" }
+
+typedef const long int L;
+
+__attribute__((no_sanitize_undefined)) void
+foo (int *p, L *l)
+{
+ int &r = *p;
+ auto &r2 = *p;
+ L &lr = *l;
+ auto &&r3 = *p;
+}
+
+struct U
+{
+ int a;
+ void foo () {}
+};
+
+__attribute__((no_sanitize_undefined)) void
+bar (U *p)
+{
+ p->foo ();
+}
+
+// { dg-final { scan-assembler-not "__ubsan_handle" } }
diff --git a/gcc/testsuite/g++.dg/ubsan/null-1.C b/gcc/testsuite/g++.dg/ubsan/null-1.C
new file mode 100644
index 0000000..e1524b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/null-1.C
@@ -0,0 +1,30 @@
+// { dg-do run }
+// { dg-options "-fsanitize=null -Wall -Wno-unused-variable -std=c++11" }
+
+typedef const long int L;
+
+int
+main (void)
+{
+ int *p = 0;
+ L *l = 0;
+
+ int &r = *p;
+ auto &r2 = *p;
+ L &lr = *l;
+
+ // Try an rvalue reference.
+ auto &&r3 = *p;
+
+ // Don't evaluate the reference initializer twice.
+ int i = 1;
+ int *q = &i;
+ int &qr = ++*q;
+ if (i != 2)
+ __builtin_abort ();
+}
+
+// { dg-output "reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'const L'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
diff --git a/gcc/testsuite/g++.dg/ubsan/null-2.C b/gcc/testsuite/g++.dg/ubsan/null-2.C
new file mode 100644
index 0000000..88f387e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/null-2.C
@@ -0,0 +1,39 @@
+// Limit this to known non-strict alignment targets.
+// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
+// { dg-options "-fsanitize=null -Wall -Wno-unused-variable -std=c++11" }
+
+#include <new>
+
+struct U
+{
+ int a;
+ void foo () {}
+};
+struct V
+{
+ V () {};
+ ~V () {};
+ int a;
+ void foo () {}
+ static void bar () {}
+};
+struct S { long int l; char buf[1 + sizeof (U) + 2 * sizeof (V)]; } s;
+
+int
+main (void)
+{
+ U *p = 0;
+ p->foo ();
+ char *q = 0;
+ V *u = new (q) V;
+ u->~V ();
+ V *v = new (q) V;
+ v->foo ();
+ v->bar (); // We don't instrument this right now.
+ v->~V ();
+}
+
+// { dg-output "\.C:26:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct U'.*" }
+// { dg-output "\.C:29:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
+// { dg-output "\.C:31:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
+// { dg-output "\.C:33:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'" }
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 4e7e487..0dbb104 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see
#include "intl.h"
#include "realmpfr.h"
#include "dfp.h"
+#include "builtins.h"
/* Map from a tree to a VAR_DECL tree. */
@@ -586,7 +587,7 @@ is_ubsan_builtin_p (tree t)
/* Expand the UBSAN_BOUNDS special builtin function. */
-void
+bool
ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
@@ -645,21 +646,52 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
/* Point GSI to next logical statement. */
*gsi = gsi_start_bb (fallthru_bb);
+ return true;
}
-/* Expand UBSAN_NULL internal call. */
+/* Expand UBSAN_NULL internal call. The type is kept on the ckind
+ argument which is a constant, because the middle-end treats pointer
+ conversions as useless and therefore the type of the first argument
+ could be changed to any other pointer type. */
-void
-ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
+bool
+ubsan_expand_null_ifn (gimple_stmt_iterator *gsip)
{
+ gimple_stmt_iterator gsi = *gsip;
gimple stmt = gsi_stmt (gsi);
location_t loc = gimple_location (stmt);
- gcc_assert (gimple_call_num_args (stmt) == 2);
+ gcc_assert (gimple_call_num_args (stmt) == 3);
tree ptr = gimple_call_arg (stmt, 0);
tree ckind = gimple_call_arg (stmt, 1);
+ tree align = gimple_call_arg (stmt, 2);
+ tree check_align = NULL_TREE;
+ bool check_null;
basic_block cur_bb = gsi_bb (gsi);
+ gimple g;
+ if (!integer_zerop (align))
+ {
+ unsigned int ptralign = get_pointer_alignment (ptr) / BITS_PER_UNIT;
+ if (compare_tree_int (align, ptralign) == 1)
+ {
+ check_align = make_ssa_name (pointer_sized_int_node, NULL);
+ g = gimple_build_assign_with_ops (NOP_EXPR, check_align,
+ ptr, NULL_TREE);
+ gimple_set_location (g, loc);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+ }
+ }
+ check_null = (flag_sanitize & SANITIZE_NULL) != 0;
+
+ if (check_align == NULL_TREE && !check_null)
+ {
+ gsi_remove (gsip, true);
+ /* Unlink the UBSAN_NULLs vops before replacing it. */
+ unlink_stmt_vdef (stmt);
+ return true;
+ }
+
/* Split the original block holding the pointer dereference. */
edge e = split_block (cur_bb, stmt);
@@ -689,12 +721,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
/* Update dominance info for the newly created then_bb; note that
fallthru_bb's dominance info has already been updated by
- split_bock. */
+ split_block. */
if (dom_info_available_p (CDI_DOMINATORS))
set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb);
/* Put the ubsan builtin call into the newly created BB. */
- gimple g;
if (flag_sanitize_undefined_trap_on_error)
g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0);
else
@@ -705,54 +736,115 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
: BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT;
tree fn = builtin_decl_implicit (bcode);
const struct ubsan_mismatch_data m
- = { build_zero_cst (pointer_sized_int_node), ckind };
+ = { align, fold_convert (unsigned_char_type_node, ckind) };
tree data
= ubsan_create_data ("__ubsan_null_data", &loc, &m,
- ubsan_type_descriptor (TREE_TYPE (ptr),
+ ubsan_type_descriptor (TREE_TYPE (ckind),
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));
+ check_align ? check_align
+ : build_zero_cst (pointer_sized_int_node));
}
- gimple_set_location (g, loc);
gimple_stmt_iterator gsi2 = gsi_start_bb (then_bb);
+ gimple_set_location (g, loc);
gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
/* Unlink the UBSAN_NULLs vops before replacing it. */
unlink_stmt_vdef (stmt);
- g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0),
- NULL_TREE, NULL_TREE);
- gimple_set_location (g, loc);
+ if (check_null)
+ {
+ g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0),
+ NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc);
- /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
- gsi_replace (&gsi, g, false);
-}
+ /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
+ gsi_replace (&gsi, g, false);
+ }
-/* Instrument a member call. We check whether 'this' is NULL. */
+ if (check_align)
+ {
+ if (check_null)
+ {
+ /* Split the block with the condition again. */
+ e = split_block (cond_bb, stmt);
+ basic_block cond1_bb = e->src;
+ basic_block cond2_bb = e->dest;
+
+ /* Make an edge coming from the 'cond1 block' into the 'then block';
+ this edge is unlikely taken, so set up the probability
+ accordingly. */
+ e = make_edge (cond1_bb, then_bb, EDGE_TRUE_VALUE);
+ e->probability = PROB_VERY_UNLIKELY;
+
+ /* Set up the fallthrough basic block. */
+ e = find_edge (cond1_bb, cond2_bb);
+ e->flags = EDGE_FALSE_VALUE;
+ e->count = cond1_bb->count;
+ e->probability = REG_BR_PROB_BASE - PROB_VERY_UNLIKELY;
+
+ /* Update dominance info. */
+ if (dom_info_available_p (CDI_DOMINATORS))
+ {
+ set_immediate_dominator (CDI_DOMINATORS, fallthru_bb, cond1_bb);
+ set_immediate_dominator (CDI_DOMINATORS, then_bb, cond1_bb);
+ }
-static void
-instrument_member_call (gimple_stmt_iterator *iter)
-{
- tree this_parm = gimple_call_arg (gsi_stmt (*iter), 0);
- tree kind = build_int_cst (unsigned_char_type_node, UBSAN_MEMBER_CALL);
- gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, this_parm, kind);
- gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
- gsi_insert_before (iter, g, GSI_SAME_STMT);
+ gsi2 = gsi_start_bb (cond2_bb);
+ }
+
+ tree mask = build_int_cst (pointer_sized_int_node,
+ tree_to_uhwi (align) - 1);
+ g = gimple_build_assign_with_ops (BIT_AND_EXPR,
+ make_ssa_name (pointer_sized_int_node,
+ NULL),
+ check_align, mask);
+ gimple_set_location (g, loc);
+ if (check_null)
+ gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
+ else
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+
+ g = gimple_build_cond (NE_EXPR, gimple_assign_lhs (g),
+ build_int_cst (pointer_sized_int_node, 0),
+ NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc);
+ if (check_null)
+ gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
+ else
+ /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
+ gsi_replace (&gsi, g, false);
+ }
+ return false;
}
-/* Instrument a memory reference. T is the pointer, IS_LHS says
+/* Instrument a memory reference. BASE is the base of MEM, IS_LHS says
whether the pointer is on the left hand side of the assignment. */
static void
-instrument_mem_ref (tree t, gimple_stmt_iterator *iter, bool is_lhs)
+instrument_mem_ref (tree mem, tree base, gimple_stmt_iterator *iter,
+ bool is_lhs)
{
enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF;
- if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t))))
+ unsigned int align = 0;
+ if (flag_sanitize & SANITIZE_ALIGNMENT)
+ {
+ align = min_align_of_type (TREE_TYPE (base));
+ if (align <= 1)
+ align = 0;
+ }
+ if (align == 0 && (flag_sanitize & SANITIZE_NULL) == 0)
+ return;
+ tree t = TREE_OPERAND (base, 0);
+ if (!POINTER_TYPE_P (TREE_TYPE (t)))
+ return;
+ if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t))) && mem != base)
ikind = UBSAN_MEMBER_ACCESS;
- tree kind = build_int_cst (unsigned_char_type_node, ikind);
- gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, t, kind);
+ tree kind = build_int_cst (TREE_TYPE (t), ikind);
+ tree alignt = build_int_cst (pointer_sized_int_node, align);
+ gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 3, t, kind, alignt);
gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
gsi_insert_before (iter, g, GSI_SAME_STMT);
}
@@ -764,15 +856,11 @@ instrument_null (gimple_stmt_iterator gsi, bool is_lhs)
{
gimple stmt = gsi_stmt (gsi);
tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
- t = get_base_address (t);
- const enum tree_code code = TREE_CODE (t);
+ tree base = get_base_address (t);
+ const enum tree_code code = TREE_CODE (base);
if (code == MEM_REF
- && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME)
- instrument_mem_ref (TREE_OPERAND (t, 0), &gsi, is_lhs);
- else if (code == ADDR_EXPR
- && POINTER_TYPE_P (TREE_TYPE (t))
- && TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == METHOD_TYPE)
- instrument_member_call (&gsi);
+ && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
+ instrument_mem_ref (t, base, &gsi, is_lhs);
}
/* Build an ubsan builtin call for the signed-integer-overflow
@@ -1147,7 +1235,8 @@ public:
virtual bool gate (function *)
{
return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
- | SANITIZE_BOOL | SANITIZE_ENUM)
+ | SANITIZE_BOOL | SANITIZE_ENUM
+ | SANITIZE_ALIGNMENT)
&& current_function_decl != NULL_TREE
&& !lookup_attribute ("no_sanitize_undefined",
DECL_ATTRIBUTES (current_function_decl));
@@ -1180,7 +1269,7 @@ pass_ubsan::execute (function *fun)
&& is_gimple_assign (stmt))
instrument_si_overflow (gsi);
- if (flag_sanitize & SANITIZE_NULL)
+ if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
{
if (gimple_store_p (stmt))
instrument_null (gsi, true);
diff --git a/gcc/ubsan.h b/gcc/ubsan.h
index 485449c..c927323 100644
--- a/gcc/ubsan.h
+++ b/gcc/ubsan.h
@@ -27,7 +27,8 @@ enum ubsan_null_ckind {
UBSAN_STORE_OF,
UBSAN_REF_BINDING,
UBSAN_MEMBER_ACCESS,
- UBSAN_MEMBER_CALL
+ UBSAN_MEMBER_CALL,
+ UBSAN_CTOR_CALL
};
/* This controls how ubsan prints types. Used in ubsan_type_descriptor. */
@@ -43,8 +44,8 @@ struct ubsan_mismatch_data {
tree ckind;
};
-extern void ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
-extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
+extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
+extern bool 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 *, ...);