From 944fa280bc92d197c443e369bb24405f007d46ab Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 1 Aug 2014 09:52:43 +0200 Subject: [PATCH] opts.c (common_handle_option): Handle -fsanitize=alignment. * 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. cp/ * 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. c-family/ * 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. testsuite/ * 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. From-SVN: r213406 --- gcc/ChangeLog | 31 ++++ gcc/asan.c | 17 +- gcc/c-family/ChangeLog | 11 ++ gcc/c-family/c-common.c | 20 --- gcc/c-family/c-common.h | 1 - gcc/c-family/c-ubsan.c | 98 +++++++++++ gcc/c-family/c-ubsan.h | 2 + gcc/cp/ChangeLog | 7 + gcc/cp/cp-gimplify.c | 21 +++ gcc/doc/invoke.texi | 10 +- gcc/flag-types.h | 3 +- gcc/opts.c | 1 + gcc/stor-layout.c | 21 +++ gcc/stor-layout.h | 3 + gcc/testsuite/ChangeLog | 15 ++ gcc/testsuite/c-c++-common/ubsan/align-1.c | 41 +++++ gcc/testsuite/c-c++-common/ubsan/align-2.c | 56 +++++++ gcc/testsuite/c-c++-common/ubsan/align-3.c | 66 ++++++++ gcc/testsuite/c-c++-common/ubsan/align-4.c | 14 ++ gcc/testsuite/c-c++-common/ubsan/align-5.c | 15 ++ gcc/testsuite/c-c++-common/ubsan/attrib-4.c | 15 ++ gcc/testsuite/g++.dg/ubsan/align-1.C | 27 ++++ gcc/testsuite/g++.dg/ubsan/align-2.C | 45 ++++++ gcc/testsuite/g++.dg/ubsan/align-3.C | 45 ++++++ gcc/testsuite/g++.dg/ubsan/attrib-1.C | 27 ++++ gcc/testsuite/g++.dg/ubsan/null-1.C | 30 ++++ gcc/testsuite/g++.dg/ubsan/null-2.C | 39 +++++ gcc/ubsan.c | 171 +++++++++++++++----- gcc/ubsan.h | 7 +- 29 files changed, 785 insertions(+), 74 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/ubsan/align-1.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/align-2.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/align-3.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/align-4.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/align-5.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/attrib-4.c create mode 100644 gcc/testsuite/g++.dg/ubsan/align-1.C create mode 100644 gcc/testsuite/g++.dg/ubsan/align-2.C create mode 100644 gcc/testsuite/g++.dg/ubsan/align-3.C create mode 100644 gcc/testsuite/g++.dg/ubsan/attrib-1.C create mode 100644 gcc/testsuite/g++.dg/ubsan/null-1.C create mode 100644 gcc/testsuite/g++.dg/ubsan/null-2.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 13e0c5e5a30..414b53b1f7a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,34 @@ +2014-08-01 Jakub Jelinek + + * 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 * tree-ssa-tail-merge.c (same_succ_hash): Convert to inchash. diff --git a/gcc/asan.c b/gcc/asan.c index 4f882b5b28a..76f21bd7020 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 bf1ad5b0cc3..55e4a66df4a 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,14 @@ +2014-08-01 Jakub Jelinek + + * 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 PR c++/60517 diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 79d0f2f214f..b2a053ed629 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 3e8c8e716f8..26aaee26cdd 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 ad5dd0bf92a..e048c53ac3e 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 edf5bc60be6..7feec45db06 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 36d8b39504b..89a6a7b9e02 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2014-08-01 Jakub Jelinek + + * 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 PR c++/60517 diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index a35177bdbda..5f5ba47848c 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 28370964ec3..7378a2e3453 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 bf813b6c6b5..135c3434bbf 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 4b0af82dac1..be1867c2388 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 109264b2ff0..1c65490ae77 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 0ff98f8f051..f7c52670a93 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 37b42b39006..cf2237113bf 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,18 @@ +2014-08-01 Jakub Jelinek + + * 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 * 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 00000000000..2e40e839261 --- /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 00000000000..071de8c202a --- /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 00000000000..a509fa992e5 --- /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 00000000000..3252595d330 --- /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 00000000000..b94e167bb67 --- /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 00000000000..ba0f00cfb7f --- /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 00000000000..65b1222a5c0 --- /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 00000000000..3e4f5485d02 --- /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 00000000000..1cc40fc4c9b --- /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 + +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 00000000000..f701d02dad3 --- /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 00000000000..e1524b1f922 --- /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 00000000000..88f387e17c9 --- /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 + +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 4e7e4878c66..0dbb104d2e8 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 485449c32ec..c9273237527 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 *, ...); -- 2.30.2