]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
* opts.c (common_handle_option): Handle -fsanitize=alignment.
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 1 Aug 2014 07:52:43 +0000 (07:52 +0000)
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 1 Aug 2014 07:52:43 +0000 (07:52 +0000)
* 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@213406 138bc75d-0d04-0410-961f-82ee72b054a4

29 files changed:
gcc/ChangeLog
gcc/asan.c
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c-family/c-ubsan.c
gcc/c-family/c-ubsan.h
gcc/cp/ChangeLog
gcc/cp/cp-gimplify.c
gcc/doc/invoke.texi
gcc/flag-types.h
gcc/opts.c
gcc/stor-layout.c
gcc/stor-layout.h
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/ubsan/align-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/align-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/align-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/align-4.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/align-5.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/attrib-4.c [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/align-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/align-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/align-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/attrib-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/null-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/null-2.C [new file with mode: 0644]
gcc/ubsan.c
gcc/ubsan.h

index 13e0c5e5a30ece7fa53f633bdbd7c21a88db369c..414b53b1f7a62d301fe97809e7ffeaa210e1319e 100644 (file)
@@ -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.
 2014-07-31  Andi Kleen  <ak@linux.intel.com>
 
        * tree-ssa-tail-merge.c (same_succ_hash): Convert to inchash.
index 4f882b5b28a3363d46368b53c10028b95ff6485d..76f21bd7020fe596a28c6b19376f370ecefa6b7e 100644 (file)
@@ -2750,21 +2750,25 @@ pass_sanopt::execute (function *fun)
   FOR_EACH_BB_FN (bb, fun)
     {
       gimple_stmt_iterator gsi;
   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);
        {
          gimple stmt = gsi_stmt (gsi);
+         bool no_next = false;
 
          if (!is_gimple_call (stmt))
 
          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:
 
          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:
                break;
              case IFN_UBSAN_BOUNDS:
-               ubsan_expand_bounds_ifn (&gsi);
+               no_next = ubsan_expand_bounds_ifn (&gsi);
                break;
              default:
                break;
                break;
              default:
                break;
@@ -2777,9 +2781,8 @@ pass_sanopt::execute (function *fun)
              fprintf (dump_file, "\n");
            }
 
              fprintf (dump_file, "\n");
            }
 
-         /* ubsan_expand_bounds_ifn might move us to the end of the BB.  */
-         if (gsi_end_p (gsi))
-           break;
+         if (!no_next)
+           gsi_next (&gsi);
        }
     }
   return 0;
        }
     }
   return 0;
index bf1ad5b0cc3c68bb324bd61fecb012046247b669..55e4a66df4ad1ffb6ef9276115daabf041d35a61 100644 (file)
@@ -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
 2014-07-31  Marc Glisse  <marc.glisse@inria.fr>
 
        PR c++/60517
index 79d0f2f214ff00a50021a2e4ac349a1409a4ec70..b2a053ed6297a540dfc1679fc1cdb9f810193a82 100644 (file)
@@ -4965,26 +4965,6 @@ c_common_get_alias_set (tree t)
   return -1;
 }
 \f
   return -1;
 }
 \f
-/* 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
 /* 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
index 3e8c8e716f86c6f0a6a54c4fcc1da4ef394d98da..26aaee26cddda516df36e7d90ed4bf24a1cfd29d 100644 (file)
@@ -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 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.
 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.
index ad5dd0bf92af5aa43fdad278316d91662b15c195..e048c53ac3ee4e3051091cff841d712cb366d089 100644 (file)
@@ -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 "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.  */
 
 /* 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;
+}
index edf5bc60be64863caa9a32743c4ff08f7564e81d..7feec45db069013826b637f04f334376b9b74121 100644 (file)
@@ -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 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  */
 
 #endif  /* GCC_C_UBSAN_H  */
index 36d8b39504b5ee92e1399324a160162607c6ebc4..89a6a7b9e0266954164e07f4a835fd2b0e7ca650 100644 (file)
@@ -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
 2014-07-31  Marc Glisse  <marc.glisse@inria.fr>
 
        PR c++/60517
index a35177bdbda799ef4d3103282ea17a627f17c7e0..5f5ba47848c56e5d34f5f39695c82372cf09072d 100644 (file)
@@ -1198,6 +1198,27 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
        *stmt_p = size_one_node;
       return NULL;
     }    
        *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);
 
 
   pointer_set_insert (p_set, *stmt_p);
 
index 28370964ec3d25bf4fb0b6cbdb5a698c0ecf9fff..7378a2e34533dd039413541ed2f30e49e87ea78b 100644 (file)
@@ -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
 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
 
 @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.
 
 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,
 @item -fsanitize=float-divide-by-zero
 @opindex fsanitize=float-divide-by-zero
 Detect floating-point division by zero.  Unlike other similar options,
index bf813b6c6b581c906cf59960810853b25e80138a..135c3434bbf659425fa38e50758cb47e35d7244e 100644 (file)
@@ -233,10 +233,11 @@ enum sanitize_code {
   SANITIZE_FLOAT_DIVIDE = 1 << 14,
   SANITIZE_FLOAT_CAST = 1 << 15,
   SANITIZE_BOUNDS = 1 << 16,
   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_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
 };
 
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
index 4b0af82dac1793a739fae07f4d24fa3e185eb7df..be1867c23882890d472b32a75d846f2a23c26119 100644 (file)
@@ -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 },
              { "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;
              { NULL, 0, 0 }
            };
            const char *comma;
index 109264b2ff020b3b14778892237e1ea204130d4b..1c65490ae777cc49b3d90aedbd0189260b078a40 100644 (file)
@@ -2390,6 +2390,27 @@ layout_type (tree type)
     gcc_assert (!TYPE_ALIAS_SET_KNOWN_P (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
 /* 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
index 0ff98f8f051c907f871690b8b38e63abf17d2d61..f7c52670a934e148798d2bcf979c29dba089dd3f 100644 (file)
@@ -59,6 +59,9 @@ extern void layout_decl (tree, unsigned);
    node, does nothing except for the first time.  */
 extern void layout_type (tree);
 
    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);
 /* 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);
index 37b42b390063f1381b9ae42f3cf3618a2ea87075..cf2237113bf4858cae03625e1c2f060304af6741 100644 (file)
@@ -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)
 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 (file)
index 0000000..2e40e83
--- /dev/null
@@ -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 (file)
index 0000000..071de8c
--- /dev/null
@@ -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 (file)
index 0000000..a509fa9
--- /dev/null
@@ -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 (file)
index 0000000..3252595
--- /dev/null
@@ -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 (file)
index 0000000..b94e167
--- /dev/null
@@ -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 (file)
index 0000000..ba0f00c
--- /dev/null
@@ -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 (file)
index 0000000..65b1222
--- /dev/null
@@ -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 (file)
index 0000000..3e4f548
--- /dev/null
@@ -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 (file)
index 0000000..1cc40fc
--- /dev/null
@@ -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 (file)
index 0000000..f701d02
--- /dev/null
@@ -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 (file)
index 0000000..e1524b1
--- /dev/null
@@ -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 (file)
index 0000000..88f387e
--- /dev/null
@@ -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'" }
index 4e7e4878c6683969ece932fa3d65e37b576d78ac..0dbb104d2e8885a646f37a7823d530759f1ce640 100644 (file)
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "realmpfr.h"
 #include "dfp.h"
 #include "intl.h"
 #include "realmpfr.h"
 #include "dfp.h"
+#include "builtins.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
 
 /* 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.  */
 
 
 /* Expand the UBSAN_BOUNDS special builtin function.  */
 
-void
+bool
 ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
 {
   gimple stmt = gsi_stmt (*gsi);
 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);
 
   /* 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);
   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 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);
 
 
   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);
 
   /* 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
 
   /* 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.  */
   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
   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
          : 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,
       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,
                                                    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_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);
 
   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
    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;
 {
   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;
     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);
 }
   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);
 {
   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
   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
 }
 
 /* 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
   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));
             && 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);
 
              && 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);
            {
              if (gimple_store_p (stmt))
                instrument_null (gsi, true);
index 485449c32eceed78587eae0589c92090b78de5dd..c92732375276795d4097e7ba3153a197077c22d5 100644 (file)
@@ -27,7 +27,8 @@ enum ubsan_null_ckind {
   UBSAN_STORE_OF,
   UBSAN_REF_BINDING,
   UBSAN_MEMBER_ACCESS,
   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.  */
 };
 
 /* This controls how ubsan prints types.  Used in ubsan_type_descriptor.  */
@@ -43,8 +44,8 @@ struct ubsan_mismatch_data {
   tree ckind;
 };
 
   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 *, ...);
 extern tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, const location_t *,
                               const struct ubsan_mismatch_data *, ...);