]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
flag-types.h (enum sanitize_code): Add SANITIZE_VPTR, include SANITIZE_VPTR in SANITI...
authorJakub Jelinek <jakub@redhat.com>
Thu, 15 Jan 2015 22:58:42 +0000 (23:58 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 15 Jan 2015 22:58:42 +0000 (23:58 +0100)
* flag-types.h (enum sanitize_code): Add SANITIZE_VPTR,
include SANITIZE_VPTR in SANITIZE_UNDEFINED.
* opts.c (common_handle_option): Add -fsanitize=vptr.
* sanitizer.def (BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS,
BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT): New.
* ubsan.h (enum ubsan_null_ckind): Add UBSAN_DOWNCAST_POINTER,
UBSAN_DOWNCAST_REFERENCE, UBSAN_UPCAST and UBSAN_CAST_TO_VBASE.
(ubsan_expand_vptr_ifn): New prototype.
* internal-fn.c (expand_ANNOTATE, expand_GOMP_SIMD_LANE,
expand_GOMP_SIMD_VF, expand_GOMP_SIMD_LAST_LANE, expand_UBSAN_NULL,
expand_UBSAN_BOUNDS, expand_UBSAN_OBJECT_SIZE, expand_ASAN_CHECK,
expand_LOOP_VECTORIZED): Make argument nameless, remove
ATTRIBUTE_UNUSED.
(expand_UBSAN_VPTR): New function.
* internal-fn.def (UBSAN_NULL, ASAN_CHECK): Use R instead of W
in fn spec.
(UBSAN_VPTR): New internal function.
* sanopt.c (tree_map_traits): Renamed to ...
(sanopt_tree_map_traits): ... this.
(sanopt_tree_triplet, sanopt_tree_triplet_map_traits): New classes.
(sanopt_ctx): Adjust asan_check_map type for tree_map_traits
to sanopt_tree_map_traits renaming.  Add vptr_check_map field.
(maybe_optimize_ubsan_vptr_ifn): New function.
(sanopt_optimize_walker): Handle IFN_UBSAN_VPTR.
(pass_sanopt::execute): Likewise.  Call sanopt_optimize even for
-fsanitize=vptr.
* tree-ssa-alias.c (call_may_clobber_ref_p_1): Handle certain
internal calls like pure functions for aliasing, even when they
have other side-effects that prevent making them ECF_PURE.
* ubsan.c (ubsan_vptr_type_cache_decl): New variable.
(ubsan_expand_vptr_ifn): New function.
cp/
* cp-gimplify.c (cp_genericize_r): Call
cp_ubsan_maybe_instrument_member_call for member calls.
(cp_ubsan_check_member_access_r): New function.
(cp_genericize_tree): Call cp_ubsan_instrument_member_accesses.
* cp-tree.h (cp_ubsan_maybe_instrument_member_call,
cp_ubsan_instrument_member_accesses,
cp_ubsan_maybe_instrument_downcast,
cp_ubsan_maybe_instrument_cast_to_vbase): New prototypes.
* cp-ubsan.c: New file.
* Make-lang.in (CXX_AND_OBJCXX_OBJS): Add cp/cp-ubsan.o.
* constexpr.c (cxx_eval_call_expression): Return void_node
for IFN_UBSAN_VPTR.
(potential_constant_expression_1): Return true for
UBSAN_NULL, UBSAN_BOUNDS and UBSAN_VPTR internal calls.
* typeck.c (build_class_member_access_expr): Provide locus
for COMPONENT_REFs.
(build_static_cast_1): Instrument downcasts.
* class.c (build_base_path): For -fsanitize=vptr and !fixed_type_p
add ubsan instrumentation for virtual_access.
* call.c: Include internal-fn.h.
(set_flags_from_callee): Handle internal calls.
gcc/testsuite/
* g++.dg/ubsan/vptr-1.C: New test.
* g++.dg/ubsan/vptr-2.C: New test.
* g++.dg/ubsan/vptr-3.C: New test.
* g++.dg/ubsan/vptr-4.C: New test.
* g++.dg/ubsan/vptr-5.C: New test.
* g++.dg/ubsan/vptr-6.C: New test.
* g++.dg/ubsan/vptr-7.C: New test.
* g++.dg/ubsan/vptr-8.C: New test.
* g++.dg/ubsan/vptr-9.C: New test.

From-SVN: r219695

29 files changed:
gcc/ChangeLog
gcc/cp/ChangeLog
gcc/cp/Make-lang.in
gcc/cp/call.c
gcc/cp/class.c
gcc/cp/constexpr.c
gcc/cp/cp-gimplify.c
gcc/cp/cp-tree.h
gcc/cp/cp-ubsan.c [new file with mode: 0644]
gcc/cp/typeck.c
gcc/flag-types.h
gcc/internal-fn.c
gcc/internal-fn.def
gcc/opts.c
gcc/sanitizer.def
gcc/sanopt.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ubsan/vptr-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/vptr-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/vptr-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/vptr-4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/vptr-5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/vptr-6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/vptr-7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/vptr-8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/vptr-9.C [new file with mode: 0644]
gcc/tree-ssa-alias.c
gcc/ubsan.c
gcc/ubsan.h

index dace3d91fbf41a190b14203a66d2fb715ceb9ef9..087bcd59f87247d65b41f97bec9cda601b216c2c 100644 (file)
@@ -1,3 +1,37 @@
+2015-01-15  Jakub Jelinek  <jakub@redhat.com>
+
+       * flag-types.h (enum sanitize_code): Add SANITIZE_VPTR,
+       include SANITIZE_VPTR in SANITIZE_UNDEFINED.
+       * opts.c (common_handle_option): Add -fsanitize=vptr.
+       * sanitizer.def (BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS,
+       BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT): New.
+       * ubsan.h (enum ubsan_null_ckind): Add UBSAN_DOWNCAST_POINTER,
+       UBSAN_DOWNCAST_REFERENCE, UBSAN_UPCAST and UBSAN_CAST_TO_VBASE.
+       (ubsan_expand_vptr_ifn): New prototype.
+       * internal-fn.c (expand_ANNOTATE, expand_GOMP_SIMD_LANE,
+       expand_GOMP_SIMD_VF, expand_GOMP_SIMD_LAST_LANE, expand_UBSAN_NULL,
+       expand_UBSAN_BOUNDS, expand_UBSAN_OBJECT_SIZE, expand_ASAN_CHECK,
+       expand_LOOP_VECTORIZED): Make argument nameless, remove
+       ATTRIBUTE_UNUSED.
+       (expand_UBSAN_VPTR): New function.
+       * internal-fn.def (UBSAN_NULL, ASAN_CHECK): Use R instead of W
+       in fn spec.
+       (UBSAN_VPTR): New internal function.
+       * sanopt.c (tree_map_traits): Renamed to ...
+       (sanopt_tree_map_traits): ... this.
+       (sanopt_tree_triplet, sanopt_tree_triplet_map_traits): New classes.
+       (sanopt_ctx): Adjust asan_check_map type for tree_map_traits
+       to sanopt_tree_map_traits renaming.  Add vptr_check_map field.
+       (maybe_optimize_ubsan_vptr_ifn): New function.
+       (sanopt_optimize_walker): Handle IFN_UBSAN_VPTR.
+       (pass_sanopt::execute): Likewise.  Call sanopt_optimize even for
+       -fsanitize=vptr.
+       * tree-ssa-alias.c (call_may_clobber_ref_p_1): Handle certain
+       internal calls like pure functions for aliasing, even when they
+       have other side-effects that prevent making them ECF_PURE.
+       * ubsan.c (ubsan_vptr_type_cache_decl): New variable.
+       (ubsan_expand_vptr_ifn): New function.
+
 2015-01-15  Vladimir Makarov  <vmakarov@redhat.com>
 
        PR rtl-optimization/64110
index 9120e07eaf97688a12dfdb1f3e49bad376d79998..889f3c1a4d5bda515d342346ae632d8d1edcc46d 100644 (file)
@@ -1,3 +1,27 @@
+2015-01-15  Jakub Jelinek  <jakub@redhat.com>
+
+       * cp-gimplify.c (cp_genericize_r): Call
+       cp_ubsan_maybe_instrument_member_call for member calls.
+       (cp_ubsan_check_member_access_r): New function.
+       (cp_genericize_tree): Call cp_ubsan_instrument_member_accesses.
+       * cp-tree.h (cp_ubsan_maybe_instrument_member_call,
+       cp_ubsan_instrument_member_accesses,
+       cp_ubsan_maybe_instrument_downcast,
+       cp_ubsan_maybe_instrument_cast_to_vbase): New prototypes.
+       * cp-ubsan.c: New file.
+       * Make-lang.in (CXX_AND_OBJCXX_OBJS): Add cp/cp-ubsan.o.
+       * constexpr.c (cxx_eval_call_expression): Return void_node
+       for IFN_UBSAN_VPTR.
+       (potential_constant_expression_1): Return true for
+       UBSAN_NULL, UBSAN_BOUNDS and UBSAN_VPTR internal calls.
+       * typeck.c (build_class_member_access_expr): Provide locus
+       for COMPONENT_REFs.
+       (build_static_cast_1): Instrument downcasts.
+       * class.c (build_base_path): For -fsanitize=vptr and !fixed_type_p
+       add ubsan instrumentation for virtual_access.
+       * call.c: Include internal-fn.h.
+       (set_flags_from_callee): Handle internal calls.
+
 2015-01-15  Momchil Velikov  <momchil.velikov@gmail.com>
 
        PR c++/59366
index e1f44912b425adc2949f50034589ebb866268829..e98beb1e33e4bcc4943361c559ae71b7eb345346 100644 (file)
@@ -78,7 +78,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \
  cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \
  cp/cp-cilkplus.o \
  cp/cp-gimplify.o cp/cp-array-notation.o cp/lambda.o \
- cp/vtable-class-hierarchy.o cp/constexpr.o $(CXX_C_OBJS)
+ cp/vtable-class-hierarchy.o cp/constexpr.o cp/cp-ubsan.o $(CXX_C_OBJS)
 
 # Language-specific object files for C++.
 CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS)
index 9568c836882f36f095f6d12f8fe458bb9df70d28..f2076c67aee090368fee08ab72fcee35470d9ead 100644 (file)
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ipa-ref.h"
 #include "cgraph.h"
 #include "wide-int.h"
+#include "internal-fn.h"
 
 /* The various kinds of conversion.  */
 
@@ -338,13 +339,16 @@ build_call_n (tree function, int n, ...)
 void
 set_flags_from_callee (tree call)
 {
-  int nothrow;
+  bool nothrow;
   tree decl = get_callee_fndecl (call);
 
   /* We check both the decl and the type; a function may be known not to
      throw without being declared throw().  */
-  nothrow = ((decl && TREE_NOTHROW (decl))
-            || TYPE_NOTHROW_P (TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (call)))));
+  nothrow = decl && TREE_NOTHROW (decl);
+  if (CALL_EXPR_FN (call))
+    nothrow |= TYPE_NOTHROW_P (TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (call))));
+  else if (internal_fn_flags (CALL_EXPR_IFN (call)) & ECF_NOTHROW)
+    nothrow = true;
 
   if (!nothrow && at_function_scope_p () && cfun && cp_function_chain)
     cp_function_chain->can_throw = 1;
index edb87fe2e0a46df0f9f8100023251cb2287ac492..1273064db3a8b44fbe394b79a29890c9f1191813 100644 (file)
@@ -447,10 +447,20 @@ build_base_path (enum tree_code code,
          v_offset = cp_build_indirect_ref (v_offset, RO_NULL, complain);
        }
       else
-       v_offset = build_vfield_ref (cp_build_indirect_ref (expr, RO_NULL,
-                                                            complain),
-                                    TREE_TYPE (TREE_TYPE (expr)));
-      
+       {
+         tree t = expr;
+         if ((flag_sanitize & SANITIZE_VPTR) && fixed_type_p == 0)
+           {
+             t = cp_ubsan_maybe_instrument_cast_to_vbase (input_location,
+                                                          probe, expr);
+             if (t == NULL_TREE)
+               t = expr;
+           }
+         v_offset = build_vfield_ref (cp_build_indirect_ref (t, RO_NULL,
+                                                             complain),
+         TREE_TYPE (TREE_TYPE (expr)));
+       }
+
       if (v_offset == error_mark_node)
        return error_mark_node;
 
index 943ecbfecb15ec37dbbdf8374755d2590f49176d..0d474245d36166d5db7ad938cff9bec1df04a219 100644 (file)
@@ -1176,6 +1176,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
       {
       case IFN_UBSAN_NULL:
       case IFN_UBSAN_BOUNDS:
+      case IFN_UBSAN_VPTR:
        return void_node;
       default:
        if (!ctx->quiet)
@@ -3820,6 +3821,19 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 
        if (fun == NULL_TREE)
          {
+           if (TREE_CODE (t) == CALL_EXPR
+               && CALL_EXPR_FN (t) == NULL_TREE)
+             switch (CALL_EXPR_IFN (t))
+               {
+               /* These should be ignored, they are optimized away from
+                  constexpr functions.  */
+               case IFN_UBSAN_NULL:
+               case IFN_UBSAN_BOUNDS:
+               case IFN_UBSAN_VPTR:
+                 return true;
+               default:
+                 break;
+               }
            /* fold_call_expr can't do anything with IFN calls.  */
            if (flags & tf_error)
              error_at (EXPR_LOC_OR_LOC (t, input_location),
index 6136b6059f5352bac710365916774f54076fc07f..4233a64aefe88da857bee8fb6e1c3cd389fbc51b 100644 (file)
@@ -1197,9 +1197,11 @@ 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))
+  else if (flag_sanitize
+          & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR))
     {
-      if (TREE_CODE (stmt) == NOP_EXPR
+      if ((flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
+         && TREE_CODE (stmt) == NOP_EXPR
          && TREE_CODE (TREE_TYPE (stmt)) == REFERENCE_TYPE)
        ubsan_maybe_instrument_reference (stmt);
       else if (TREE_CODE (stmt) == CALL_EXPR)
@@ -1214,7 +1216,10 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
                = 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);
+             if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
+               ubsan_maybe_instrument_member_call (stmt, is_ctor);
+             if ((flag_sanitize & SANITIZE_VPTR) && !is_ctor)
+               cp_ubsan_maybe_instrument_member_call (stmt);
            }
        }
     }
@@ -1237,6 +1242,8 @@ cp_genericize_tree (tree* t_p)
   cp_walk_tree (t_p, cp_genericize_r, &wtd, NULL);
   delete wtd.p_set;
   wtd.bind_expr_stack.release ();
+  if (flag_sanitize & SANITIZE_VPTR)
+    cp_ubsan_instrument_member_accesses (t_p);
 }
 
 /* If a function that should end with a return in non-void
index 10c63fd1b4dc2e13c631725ed79a64fb771782d6..1176583cef77f42b5553b11d5631474b74376b77 100644 (file)
@@ -6412,6 +6412,12 @@ extern vec<tree> cx_error_context               (void);
 /* In c-family/cilk.c */
 extern bool cilk_valid_spawn                    (tree);
 
+/* In cp-ubsan.c */
+extern void cp_ubsan_maybe_instrument_member_call (tree);
+extern void cp_ubsan_instrument_member_accesses (tree *);
+extern tree cp_ubsan_maybe_instrument_downcast (location_t, tree, tree);
+extern tree cp_ubsan_maybe_instrument_cast_to_vbase (location_t, tree, tree);
+
 /* -- end of C++ */
 
 #endif /* ! GCC_CP_TREE_H */
diff --git a/gcc/cp/cp-ubsan.c b/gcc/cp/cp-ubsan.c
new file mode 100644 (file)
index 0000000..69e4864
--- /dev/null
@@ -0,0 +1,302 @@
+/* UndefinedBehaviorSanitizer, undefined behavior detector.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+   Contributed by Jakub Jelinek <jakub@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "options.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "tree.h"
+#include "alloc-pool.h"
+#include "output.h"
+#include "toplev.h"
+#include "ubsan.h"
+#include "cp-tree.h"
+#include "c-family/c-common.h"
+#include "c-family/c-ubsan.h"
+#include "asan.h"
+#include "internal-fn.h"
+#include "stor-layout.h"
+#include "builtins.h"
+#include "fold-const.h"
+#include "stringpool.h"
+#include "is-a.h"
+#include "predict.h"
+#include "tree-ssa-alias.h"
+#include "basic-block.h"
+#include "gimple-expr.h"
+#include "gimple.h"
+#include "ipa-ref.h"
+#include "lto-streamer.h"
+#include "cgraph.h"
+
+/* Test if we should instrument vptr access.  */
+
+static bool
+cp_ubsan_instrument_vptr_p (tree type)
+{
+  if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
+    return false;
+
+  if (current_function_decl
+      && lookup_attribute ("no_sanitize_undefined",
+                          DECL_ATTRIBUTES (current_function_decl)))
+    return false;
+
+  if (type)
+    {
+      type = TYPE_MAIN_VARIANT (type);
+      if (!CLASS_TYPE_P (type) || !CLASSTYPE_VTABLES (type))
+       return false;
+    }
+
+  return true;
+}
+
+/* Helper function for
+   cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
+   Instrument vptr access.  */
+
+static tree
+cp_ubsan_instrument_vptr (location_t loc, tree op, tree type, bool is_addr,
+                         enum ubsan_null_ckind ckind)
+{
+  type = TYPE_MAIN_VARIANT (type);
+  const char *mangled = mangle_type_string (type);
+  hashval_t str_hash1 = htab_hash_string (mangled);
+  hashval_t str_hash2 = iterative_hash (mangled, strlen (mangled), 0);
+  tree str_hash = wide_int_to_tree (uint64_type_node,
+                                   wi::uhwi (((uint64_t) str_hash1 << 32)
+                                             | str_hash2, 64));
+  if (!is_addr)
+    op = build_fold_addr_expr_loc (loc, op);
+  op = save_expr (op);
+  tree vptr = fold_build3_loc (loc, COMPONENT_REF,
+                              TREE_TYPE (TYPE_VFIELD (type)),
+                              build_fold_indirect_ref_loc (loc, op),
+                              TYPE_VFIELD (type), NULL_TREE);
+  vptr = fold_convert_loc (loc, pointer_sized_int_node, vptr);
+  vptr = fold_convert_loc (loc, uint64_type_node, vptr);
+  if (ckind == UBSAN_DOWNCAST_POINTER)
+    vptr = fold_build3 (COND_EXPR, uint64_type_node,
+                       fold_build2 (NE_EXPR, boolean_type_node, op,
+                                    build_zero_cst (TREE_TYPE (op))),
+                       vptr, build_int_cst (uint64_type_node, 0));
+  vptr = build1_loc (loc, SAVE_EXPR, uint64_type_node, vptr);
+  tree ti_decl = get_tinfo_decl (type);
+  mark_used (ti_decl);
+  tree ptype = build_pointer_type (type);
+  tree call
+    = build_call_expr_internal_loc (loc, IFN_UBSAN_VPTR,
+                                   void_type_node, 5, op, vptr, str_hash,
+                                   build_address (ti_decl),
+                                   build_int_cst (ptype, ckind));
+  TREE_SIDE_EFFECTS (call) = 1;
+  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op);
+}
+
+/* Helper function for
+   cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
+   Instrument vptr access if it should be instrumented, otherwise return
+   NULL_TREE.  */
+
+static tree
+cp_ubsan_maybe_instrument_vptr (location_t loc, tree op, tree type,
+                               bool is_addr, enum ubsan_null_ckind ckind)
+{
+  if (!cp_ubsan_instrument_vptr_p (type))
+    return NULL_TREE;
+  return cp_ubsan_instrument_vptr (loc, op, type, is_addr, ckind);
+}
+
+/* Instrument a member call (but not constructor call) if needed.  */
+
+void
+cp_ubsan_maybe_instrument_member_call (tree stmt)
+{
+  if (call_expr_nargs (stmt) == 0)
+    return;
+  tree *opp = &CALL_EXPR_ARG (stmt, 0);
+  tree op = *opp;
+  if (op == error_mark_node
+      || !POINTER_TYPE_P (TREE_TYPE (op)))
+    return;
+  while (TREE_CODE (op) == COMPOUND_EXPR)
+    {
+      opp = &TREE_OPERAND (op, 1);
+      op = *opp;
+    }
+  op = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), op,
+                                      TREE_TYPE (TREE_TYPE (op)),
+                                      true, UBSAN_MEMBER_CALL);
+  if (op)
+    *opp = op;
+}
+
+/* Data passed to cp_ubsan_check_member_access_r.  */
+
+struct cp_ubsan_check_member_access_data
+{
+  hash_set<tree> *pset;
+  bool is_addr;
+};
+
+static tree cp_ubsan_check_member_access_r (tree *, int *, void *);
+
+/* Instrument a member access.  */
+
+static bool
+cp_ubsan_maybe_instrument_member_access
+     (tree stmt, cp_ubsan_check_member_access_data *ucmd)
+{
+  if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1)))
+    return false;
+
+  tree base = TREE_OPERAND (stmt, 0);
+  if (!cp_ubsan_instrument_vptr_p (TREE_TYPE (base)))
+    return false;
+
+  cp_walk_tree (&base, cp_ubsan_check_member_access_r, ucmd, ucmd->pset);
+
+  base = cp_ubsan_instrument_vptr (EXPR_LOCATION (stmt), base,
+                                  TREE_TYPE (base), false,
+                                  UBSAN_MEMBER_ACCESS);
+  TREE_OPERAND (stmt, 0)
+    = build_fold_indirect_ref_loc (EXPR_LOCATION (stmt), base);
+  return true;
+}
+
+/* Attempt to instrument member accesses inside of the function.
+   cp_ubsan_maybe_instrument_member_access should be called on COMPONENT_REFs
+   in the GENERIC IL, but only when the field is actually accessed, not
+   merely when it's address is taken.  Therefore we track in is_addr field
+   whether in the current context we are processing address taken
+   handled components or not.  E.g. for &x->y[w->z] we want to call
+   cp_ubsan_maybe_instrument_member_access on *w.z COMPONENT_REF, but
+   not on *x.y.  */
+
+static tree
+cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
+{
+  tree stmt = *stmt_p, t;
+  cp_ubsan_check_member_access_data *ucmd
+    = (cp_ubsan_check_member_access_data *) data;
+  switch (TREE_CODE (stmt))
+    {
+    case ADDR_EXPR:
+      t = TREE_OPERAND (stmt, 0);
+      while ((TREE_CODE (t) == MEM_REF || TREE_CODE (t) == INDIRECT_REF)
+            && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR)
+       t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+      if (handled_component_p (t))
+       {
+         *walk_subtrees = 0;
+         ucmd->is_addr = true;
+         cp_walk_tree (&t, cp_ubsan_check_member_access_r,
+                       data, ucmd->pset);
+         ucmd->is_addr = false;
+       }
+      break;
+    case MEM_REF:
+    case INDIRECT_REF:
+      t = TREE_OPERAND (stmt, 0);
+      if (TREE_CODE (t) == ADDR_EXPR)
+       {
+         *walk_subtrees = 0;
+         t = TREE_OPERAND (stmt, 0);
+         cp_walk_tree (&t, cp_ubsan_check_member_access_r, data, ucmd->pset);
+       }
+      break;
+    case COMPONENT_REF:
+      if (!ucmd->is_addr && cp_ubsan_maybe_instrument_member_access (stmt, ucmd))
+       {
+         *walk_subtrees = 0;
+         break;
+       }
+      /* FALLTHRU */
+    default:
+      if (ucmd->is_addr && handled_component_p (stmt))
+       {
+         int i, len = TREE_OPERAND_LENGTH (stmt);
+         *walk_subtrees = 0;
+         if (!handled_component_p (TREE_OPERAND (stmt, 0)))
+           ucmd->is_addr = false;
+         for (i = 0; i < len; i++)
+           {
+             cp_walk_tree (&TREE_OPERAND (stmt, i),
+                           cp_ubsan_check_member_access_r, data, ucmd->pset);
+             ucmd->is_addr = false;
+           }
+         ucmd->is_addr = true;
+       }
+      break;
+    }
+  return NULL_TREE;
+}
+
+/* Instrument all member accesses inside GENERIC *T_P.  */
+
+void
+cp_ubsan_instrument_member_accesses (tree *t_p)
+{
+  if (cp_ubsan_instrument_vptr_p (NULL_TREE))
+    {
+      hash_set<tree> pset;
+      cp_ubsan_check_member_access_data ucmd;
+      ucmd.pset = &pset;
+      ucmd.is_addr = false;
+      cp_walk_tree (t_p, cp_ubsan_check_member_access_r, &ucmd, &pset);
+    }
+}
+
+/* Instrument downcast.  */
+
+tree
+cp_ubsan_maybe_instrument_downcast (location_t loc, tree type, tree op)
+{
+  if (!POINTER_TYPE_P (type)
+      || !POINTER_TYPE_P (TREE_TYPE (op))
+      || !CLASS_TYPE_P (TREE_TYPE (type))
+      || !CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (op)))
+      || !DERIVED_FROM_P (TREE_TYPE (TREE_TYPE (op)), TREE_TYPE (type)))
+    return NULL_TREE;
+
+  return cp_ubsan_maybe_instrument_vptr (loc, op, TREE_TYPE (type), true,
+                                        TREE_CODE (type) == POINTER_TYPE
+                                        ? UBSAN_DOWNCAST_POINTER
+                                        : UBSAN_DOWNCAST_REFERENCE);
+}
+
+/* Instrument cast to virtual base.  */
+
+tree
+cp_ubsan_maybe_instrument_cast_to_vbase (location_t loc, tree type, tree op)
+{
+  return cp_ubsan_maybe_instrument_vptr (loc, op, type, true,
+                                        UBSAN_CAST_TO_VBASE);
+}
index 69e22290765f31e476b0baba14b02fbf0d84fa68..32ee78ec307014f36d6bca98019d308331d25406 100644 (file)
@@ -2425,8 +2425,8 @@ build_class_member_access_expr (tree object, tree member,
          member_type = cp_build_qualified_type (member_type, type_quals);
        }
 
-      result = build3 (COMPONENT_REF, member_type, object, member,
-                      NULL_TREE);
+      result = build3_loc (input_location, COMPONENT_REF, member_type,
+                          object, member, NULL_TREE);
       result = fold_if_not_in_template (result);
 
       /* Mark the expression const or volatile, as appropriate.  Even
@@ -6474,11 +6474,21 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
       base = lookup_base (TREE_TYPE (type), intype,
                          c_cast_p ? ba_unique : ba_check,
                          NULL, complain);
+      expr = build_address (expr);
+
+      if (flag_sanitize & SANITIZE_VPTR)
+       {
+         tree ubsan_check
+           = cp_ubsan_maybe_instrument_downcast (input_location, type, expr);
+         if (ubsan_check)
+           expr = ubsan_check;
+       }
 
       /* Convert from "B*" to "D*".  This function will check that "B"
         is not a virtual base of "D".  */
-      expr = build_base_path (MINUS_EXPR, build_address (expr),
-                             base, /*nonnull=*/false, complain);
+      expr = build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false,
+                             complain);
+
       /* Convert the pointer to a reference -- but then remember that
         there are no expressions with reference type in C++.
 
@@ -6606,7 +6616,16 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
                          NULL, complain);
       expr = build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false,
                              complain);
-      return cp_fold_convert(type, expr);
+
+      if (flag_sanitize & SANITIZE_VPTR)
+       {
+         tree ubsan_check
+           = cp_ubsan_maybe_instrument_downcast (input_location, type, expr);
+         if (ubsan_check)
+           expr = ubsan_check;
+       }
+
+      return cp_fold_convert (type, expr);
     }
 
   if ((TYPE_PTRDATAMEM_P (type) && TYPE_PTRDATAMEM_P (intype))
index 6588a7b645a9004907219540f48e33a2f2e7a1e1..bfdce442a629854556c93d2e98bff322d1841f3c 100644 (file)
@@ -237,13 +237,14 @@ enum sanitize_code {
   SANITIZE_NONNULL_ATTRIBUTE = 1UL << 18,
   SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1UL << 19,
   SANITIZE_OBJECT_SIZE = 1UL << 20,
+  SANITIZE_VPTR = 1UL << 21,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
                       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
                       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
                       | SANITIZE_BOUNDS | SANITIZE_ALIGNMENT
                       | SANITIZE_NONNULL_ATTRIBUTE
                       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
-                      | SANITIZE_OBJECT_SIZE,
+                      | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
index 0609e4a34c7c303429efbfef2a59a7274904d35f..e4028250c2acecb6387eefee44b56efdf3b404a5 100644 (file)
@@ -166,7 +166,7 @@ expand_STORE_LANES (gcall *stmt)
 }
 
 static void
-expand_ANNOTATE (gcall *stmt ATTRIBUTE_UNUSED)
+expand_ANNOTATE (gcall *)
 {
   gcc_unreachable ();
 }
@@ -174,7 +174,7 @@ expand_ANNOTATE (gcall *stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in adjust_simduid_builtins.  */
 
 static void
-expand_GOMP_SIMD_LANE (gcall *stmt ATTRIBUTE_UNUSED)
+expand_GOMP_SIMD_LANE (gcall *)
 {
   gcc_unreachable ();
 }
@@ -182,7 +182,7 @@ expand_GOMP_SIMD_LANE (gcall *stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in adjust_simduid_builtins.  */
 
 static void
-expand_GOMP_SIMD_VF (gcall *stmt ATTRIBUTE_UNUSED)
+expand_GOMP_SIMD_VF (gcall *)
 {
   gcc_unreachable ();
 }
@@ -190,7 +190,7 @@ expand_GOMP_SIMD_VF (gcall *stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in adjust_simduid_builtins.  */
 
 static void
-expand_GOMP_SIMD_LAST_LANE (gcall *stmt ATTRIBUTE_UNUSED)
+expand_GOMP_SIMD_LAST_LANE (gcall *)
 {
   gcc_unreachable ();
 }
@@ -198,7 +198,7 @@ expand_GOMP_SIMD_LAST_LANE (gcall *stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_NULL (gcall *stmt ATTRIBUTE_UNUSED)
+expand_UBSAN_NULL (gcall *)
 {
   gcc_unreachable ();
 }
@@ -206,7 +206,7 @@ expand_UBSAN_NULL (gcall *stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_BOUNDS (gcall *stmt ATTRIBUTE_UNUSED)
+expand_UBSAN_BOUNDS (gcall *)
 {
   gcc_unreachable ();
 }
@@ -214,7 +214,7 @@ expand_UBSAN_BOUNDS (gcall *stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_OBJECT_SIZE (gcall *stmt ATTRIBUTE_UNUSED)
+expand_UBSAN_VPTR (gcall *)
 {
   gcc_unreachable ();
 }
@@ -222,7 +222,15 @@ expand_UBSAN_OBJECT_SIZE (gcall *stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_ASAN_CHECK (gcall *stmt ATTRIBUTE_UNUSED)
+expand_UBSAN_OBJECT_SIZE (gcall *)
+{
+  gcc_unreachable ();
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_CHECK (gcall *)
 {
   gcc_unreachable ();
 }
@@ -1889,7 +1897,7 @@ expand_MUL_OVERFLOW (gcall *stmt)
 /* This should get folded in tree-vectorizer.c.  */
 
 static void
-expand_LOOP_VECTORIZED (gcall *stmt ATTRIBUTE_UNUSED)
+expand_LOOP_VECTORIZED (gcall *)
 {
   gcc_unreachable ();
 }
index f7ebce34225b32296ccfecebc95c5e412219cb98..032ce6c909e5a4a872e010ac89d82e43f1e449f6 100644 (file)
@@ -48,15 +48,16 @@ DEF_INTERNAL_FN (LOOP_VECTORIZED, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF, NULL)
 DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF, NULL)
 DEF_INTERNAL_FN (ANNOTATE,  ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
-DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW, ".W.")
+DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW, ".R.")
 DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (UBSAN_VPTR, ECF_LEAF | ECF_NOTHROW, ".RR..")
 DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
-DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W...")
+DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R...")
 DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
index 8a16116665f1bbc00eda0fba566b62f894ddb1be..305e349497201526b4a0231abd114ff77972fba7 100644 (file)
@@ -1588,6 +1588,7 @@ common_handle_option (struct gcc_options *opts,
                sizeof "returns-nonnull-attribute" - 1 },
              { "object-size", SANITIZE_OBJECT_SIZE,
                sizeof "object-size" - 1 },
+             { "vptr", SANITIZE_VPTR, sizeof "vptr" - 1 },
              { "all", ~0, sizeof "all" - 1 },
              { NULL, 0, 0 }
            };
index b0f423d58b0c591ca9991af7c13307aef371bf75..0f18928810785de4504b2eaec5fa206edfac370d 100644 (file)
@@ -499,3 +499,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT,
                      "__ubsan_handle_nonnull_return_abort",
                      BT_FN_VOID_PTR,
                      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS,
+                     "__ubsan_handle_dynamic_type_cache_miss",
+                     BT_FN_VOID_PTR_PTR_PTR,
+                     ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT,
+                     "__ubsan_handle_dynamic_type_cache_miss_abort",
+                     BT_FN_VOID_PTR_PTR_PTR,
+                     ATTR_COLD_NOTHROW_LEAF_LIST)
index fa25890ef4797724d94228a641a6b7176d0c1d54..9689aef4f0a4c65ee613a7e318c9a474d1907238 100644 (file)
@@ -108,7 +108,7 @@ maybe_get_single_definition (tree t)
 
 /* Traits class for tree hash maps below.  */
 
-struct tree_map_traits : default_hashmap_traits
+struct sanopt_tree_map_traits : default_hashmap_traits
 {
   static inline hashval_t hash (const_tree ref)
   {
@@ -121,6 +121,63 @@ struct tree_map_traits : default_hashmap_traits
   }
 }; 
 
+/* Tree triplet for vptr_check_map.  */
+struct sanopt_tree_triplet
+{
+  tree t1, t2, t3;
+};
+
+/* Traits class for tree triplet hash maps below.  */
+
+struct sanopt_tree_triplet_map_traits : default_hashmap_traits
+{
+  static inline hashval_t
+  hash (const sanopt_tree_triplet &ref)
+  {
+    inchash::hash hstate (0);
+    inchash::add_expr (ref.t1, hstate);
+    inchash::add_expr (ref.t2, hstate);
+    inchash::add_expr (ref.t3, hstate);
+    return hstate.end ();
+  }
+
+  static inline bool
+  equal_keys (const sanopt_tree_triplet &ref1, const sanopt_tree_triplet &ref2)
+  {
+    return operand_equal_p (ref1.t1, ref2.t1, 0)
+          && operand_equal_p (ref1.t2, ref2.t2, 0)
+          && operand_equal_p (ref1.t3, ref2.t3, 0);
+  }
+
+  template<typename T>
+  static inline void
+  mark_deleted (T &e)
+  {
+    e.m_key.t1 = reinterpret_cast<T *> (1);
+  }
+
+  template<typename T>
+  static inline void
+  mark_empty (T &e)
+  {
+    e.m_key.t1 = NULL;
+  }
+
+  template<typename T>
+  static inline bool
+  is_deleted (T &e)
+  {
+    return e.m_key.t1 == (void *) 1;
+  }
+
+  template<typename T>
+  static inline bool
+  is_empty (T &e)
+  {
+    return e.m_key.t1 == NULL;
+  }
+};
+
 /* This is used to carry various hash maps and variables used
    in sanopt_optimize_walker.  */
 
@@ -132,7 +189,13 @@ struct sanopt_ctx
 
   /* This map maps a pointer (the second argument of ASAN_CHECK) to
      a vector of ASAN_CHECK call statements that check the access.  */
-  hash_map<tree, auto_vec<gimple>, tree_map_traits> asan_check_map;
+  hash_map<tree, auto_vec<gimple>, sanopt_tree_map_traits> asan_check_map;
+
+  /* This map maps a tree triplet (the first, second and fourth argument
+     of UBSAN_VPTR) to a vector of UBSAN_VPTR call statements that check
+     that virtual table pointer.  */
+  hash_map<sanopt_tree_triplet, auto_vec<gimple>,
+          sanopt_tree_triplet_map_traits> vptr_check_map;
 
   /* Number of IFN_ASAN_CHECK statements.  */
   int asan_num_accesses;
@@ -306,6 +369,32 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
   return remove;
 }
 
+/* Optimize away redundant UBSAN_VPTR calls.  The second argument
+   is the value loaded from the virtual table, so rely on FRE to find out
+   when we can actually optimize.  */
+
+static bool
+maybe_optimize_ubsan_vptr_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+  gcc_assert (gimple_call_num_args (stmt) == 5);
+  sanopt_tree_triplet triplet;
+  triplet.t1 = gimple_call_arg (stmt, 0);
+  triplet.t2 = gimple_call_arg (stmt, 1);
+  triplet.t3 = gimple_call_arg (stmt, 3);
+
+  auto_vec<gimple> &v = ctx->vptr_check_map.get_or_insert (triplet);
+  gimple g = maybe_get_dominating_check (v);
+  if (!g)
+    {
+      /* For this PTR we don't have any UBSAN_VPTR stmts recorded, so there's
+        nothing to optimize yet.  */
+      v.safe_push (stmt);
+      return false;
+    }
+
+  return true;
+}
+
 /* Returns TRUE if ASan check of length LEN in block BB can be removed
    if preceded by checks in V.  */
 
@@ -497,6 +586,9 @@ sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
          case IFN_UBSAN_NULL:
            remove = maybe_optimize_ubsan_null_ifn (ctx, stmt);
            break;
+         case IFN_UBSAN_VPTR:
+           remove = maybe_optimize_ubsan_vptr_ifn (ctx, stmt);
+           break;
          case IFN_ASAN_CHECK:
            if (asan_check_optimize)
              remove = maybe_optimize_asan_check_ifn (ctx, stmt);
@@ -601,7 +693,8 @@ pass_sanopt::execute (function *fun)
   /* Try to remove redundant checks.  */
   if (optimize
       && (flag_sanitize
-         & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_ADDRESS)))
+         & (SANITIZE_NULL | SANITIZE_ALIGNMENT
+            | SANITIZE_ADDRESS | SANITIZE_VPTR)))
     asan_num_accesses = sanopt_optimize (fun);
   else if (flag_sanitize & SANITIZE_ADDRESS)
     {
@@ -647,6 +740,9 @@ pass_sanopt::execute (function *fun)
                case IFN_UBSAN_OBJECT_SIZE:
                  no_next = ubsan_expand_objsize_ifn (&gsi);
                  break;
+               case IFN_UBSAN_VPTR:
+                 no_next = ubsan_expand_vptr_ifn (&gsi);
+                 break;
                case IFN_ASAN_CHECK:
                  no_next = asan_expand_check_ifn (&gsi, use_calls);
                  break;
index b47f7510a07aea819d5a8852c2ad1e1b4bc0d1a7..8a9f9271080e1f53ca086bc85b0b566745e4ac54 100644 (file)
@@ -1,3 +1,15 @@
+2015-01-15  Jakub Jelinek  <jakub@redhat.com>
+
+       * g++.dg/ubsan/vptr-1.C: New test.
+       * g++.dg/ubsan/vptr-2.C: New test.
+       * g++.dg/ubsan/vptr-3.C: New test.
+       * g++.dg/ubsan/vptr-4.C: New test.
+       * g++.dg/ubsan/vptr-5.C: New test.
+       * g++.dg/ubsan/vptr-6.C: New test.
+       * g++.dg/ubsan/vptr-7.C: New test.
+       * g++.dg/ubsan/vptr-8.C: New test.
+       * g++.dg/ubsan/vptr-9.C: New test.
+
 2015-01-15  Eric Botcazou  <ebotcazou@adacore.com>
 
        * lib/c-torture.exp: Compute LTO_TORTURE_OPTIONS after the environment
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-1.C b/gcc/testsuite/g++.dg/ubsan/vptr-1.C
new file mode 100644 (file)
index 0000000..f4260c1
--- /dev/null
@@ -0,0 +1,184 @@
+// { dg-do run { target { ilp32 || lp64 } } }
+// { dg-options "-fsanitize=vptr" }
+
+struct S
+{
+  S() : a(0) {}
+  ~S() {}
+  int a;
+  int f() { return 0; }
+  virtual int v() { return 0; }
+};
+
+struct T : S
+{
+  T() : b(0) {}
+  int b;
+  int g() { return 0; }
+  virtual int v() { return 1; }
+};
+
+struct U : S, T { virtual int v() { return 2; } }; // { dg-warning "direct base .S. inaccessible in .U. due to ambiguity" }
+struct V : S {};
+
+void
+foo ()
+{
+  T t;
+  (void)t.a;
+  (void)t.b;
+  (void)t.f();
+  (void)t.g();
+  (void)t.v();
+  (void)t.S::v();
+
+  U u;
+  (void)u.T::a;
+  (void)u.b;
+  (void)u.T::f();
+  (void)u.g();
+  (void)u.v();
+  (void)u.T::v();
+  (void)((T&)u).S::v();
+}
+
+T *x;
+
+__attribute__((noinline, noclone)) int
+bar (T *p, int q)
+{
+  switch (q)
+    {
+    // These shouldn't fail:
+    case 0x10:
+    case 0x20:
+    case 0x30:
+    case 0x40:
+      {
+       T &r = *p;
+       break;
+      }
+    case 0x21:
+    case 0x31:
+      return p->b;
+    case 0x22:
+    case 0x32:
+      return p->g ();
+    case 0x23:
+    case 0x33:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    case 0x44:
+      return reinterpret_cast<U*>(p)->v() - 2;
+    // These should:
+    case 0x11:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-1.C:75:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x12:
+      return p->g ();
+    // { dg-output "\[^\n\r]*vptr-1.C:82:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x13:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    // { dg-output "\[^\n\r]*vptr-1.C:89:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x34:
+      return reinterpret_cast<U*>(p)->v() - 2;
+    // { dg-output "\[^\n\r]*vptr-1.C:97:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'U'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 16 within object of type 'U'(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 8 within object of type 'U'(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^                                                 ~~~~~~~~~~~~~~~~~~~~~~~(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "                                                                vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "              \\^                        ~~~~~~~~~~~(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "                                       vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    case 0x41:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-1.C:107:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x42:
+      return p->g ();
+    // { dg-output "\[^\n\r]*vptr-1.C:114:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x43:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    // { dg-output "\[^\n\r]*vptr-1.C:121:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x51:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-1.C:129:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object has invalid vptr(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  00 00 00 00 00 00 00 00  \[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "              \\^~~~~~~~~~~~~~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "  ?.. .. .. ..  ?00 00 00 00  ?.. .. .. ..  ?\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "              invalid vptr" }
+    }
+  return 0;
+}
+
+char b[sizeof (U)] __attribute__((aligned (__alignof__ (U)))) = {};
+
+__attribute__((noinline, noclone)) void
+baz (int q)
+{
+  T *p = 0;
+  S *s = 0;
+  U *u = 0;
+  switch (q)
+    {
+    case 0x10: case 0x11: case 0x12: case 0x13:
+      s = new S;
+      bar (reinterpret_cast<T *>(s), q);
+      delete s;
+      break;
+    case 0x20: case 0x21: case 0x22: case 0x23:
+      p = new T;
+      bar (p, q);
+      delete p;
+      break;
+    case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+      u = new U;
+      bar (u, q);
+      delete u;
+      break;
+    case 0x40: case 0x41: case 0x42: case 0x43: case 0x44:
+      u = new U;
+      bar (reinterpret_cast<T *>(u), q);
+      delete u;
+      break;
+    case 0x51:
+      p = reinterpret_cast<T*>(b);
+      bar (p, q);
+      break;
+    }
+}
+
+int
+main ()
+{
+  foo ();
+  for (int q = 0; q < 0x52; q++)
+    baz (q);
+}
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-2.C b/gcc/testsuite/g++.dg/ubsan/vptr-2.C
new file mode 100644 (file)
index 0000000..2aa7046
--- /dev/null
@@ -0,0 +1,184 @@
+// { dg-do run { target { ilp32 || lp64 } } }
+// { dg-options "-fsanitize=vptr" }
+
+struct S
+{
+  S() : a(0) {}
+  ~S() {}
+  int a;
+  int f() { return 0; }
+  virtual int v() { return 0; }
+};
+
+struct T : S
+{
+  T() : b(0) {}
+  int b;
+  int g() { return 0; }
+  virtual int v() { return 1; }
+};
+
+struct U : S, T { virtual int v() { return 2; } }; // { dg-warning "direct base .S. inaccessible in .U. due to ambiguity" }
+struct V : S {};
+
+void
+foo ()
+{
+  T t;
+  (void)t.a;
+  (void)t.b;
+  (void)t.f();
+  (void)t.g();
+  (void)t.v();
+  (void)t.S::v();
+
+  U u;
+  (void)u.T::a;
+  (void)u.b;
+  (void)u.T::f();
+  (void)u.g();
+  (void)u.v();
+  (void)u.T::v();
+  (void)((T&)u).S::v();
+}
+
+T *x;
+template <typename S, typename T, typename U>
+__attribute__((noinline, noclone)) int
+bar (T *p, int q)
+{
+  switch (q)
+    {
+    // These shouldn't fail:
+    case 0x10:
+    case 0x20:
+    case 0x30:
+    case 0x40:
+      {
+       T &r = *p;
+       break;
+      }
+    case 0x21:
+    case 0x31:
+      return p->b;
+    case 0x22:
+    case 0x32:
+      return p->g ();
+    case 0x23:
+    case 0x33:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    case 0x44:
+      return reinterpret_cast<U*>(p)->v() - 2;
+    // These should:
+    case 0x11:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-2.C:75:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x12:
+      return p->g ();
+    // { dg-output "\[^\n\r]*vptr-2.C:82:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x13:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    // { dg-output "\[^\n\r]*vptr-2.C:89:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x34:
+      return reinterpret_cast<U*>(p)->v() - 2;
+    // { dg-output "\[^\n\r]*vptr-2.C:97:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'U'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 16 within object of type 'U'(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 8 within object of type 'U'(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^                                                 ~~~~~~~~~~~~~~~~~~~~~~~(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "                                                                vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "              \\^                        ~~~~~~~~~~~(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "                                       vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    case 0x41:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-2.C:107:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x42:
+      return p->g ();
+    // { dg-output "\[^\n\r]*vptr-2.C:114:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x43:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    // { dg-output "\[^\n\r]*vptr-2.C:121:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x51:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-2.C:129:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object has invalid vptr(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  00 00 00 00 00 00 00 00  \[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "              \\^~~~~~~~~~~~~~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "  ?.. .. .. ..  ?00 00 00 00  ?.. .. .. ..  ?\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "              invalid vptr" }
+    }
+  return 0;
+}
+
+char b[sizeof (U)] __attribute__((aligned (__alignof__ (U)))) = {};
+
+__attribute__((noinline, noclone)) void
+baz (int q)
+{
+  T *p = 0;
+  S *s = 0;
+  U *u = 0;
+  switch (q)
+    {
+    case 0x10: case 0x11: case 0x12: case 0x13:
+      s = new S;
+      bar<S, T, U> (reinterpret_cast<T *>(s), q);
+      delete s;
+      break;
+    case 0x20: case 0x21: case 0x22: case 0x23:
+      p = new T;
+      bar<S, T, U> (p, q);
+      delete p;
+      break;
+    case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+      u = new U;
+      bar<S, T, U> (u, q);
+      delete u;
+      break;
+    case 0x40: case 0x41: case 0x42: case 0x43: case 0x44:
+      u = new U;
+      bar<S, T, U> (reinterpret_cast<T *>(u), q);
+      delete u;
+      break;
+    case 0x51:
+      p = reinterpret_cast<T*>(b);
+      bar<S, T, U> (p, q);
+      break;
+    }
+}
+
+int
+main ()
+{
+  foo ();
+  for (int q = 0; q < 0x52; q++)
+    baz (q);
+}
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-3.C b/gcc/testsuite/g++.dg/ubsan/vptr-3.C
new file mode 100644 (file)
index 0000000..916d8ef
--- /dev/null
@@ -0,0 +1,184 @@
+// { dg-do run { target { ilp32 || lp64 } } }
+// { dg-options "-fsanitize=vptr" }
+
+struct S
+{
+  S() : a(0) {}
+  ~S() {}
+  int a;
+  int f() { return 0; }
+  virtual int v() { return 0; }
+};
+
+struct T : S
+{
+  T() : b(0) {}
+  int b;
+  int g() { return 0; }
+  virtual int v() { return 1; }
+};
+
+struct U : S, T { virtual int v() { return 2; } }; // { dg-warning "direct base .S. inaccessible in .U. due to ambiguity" }
+struct V : S {};
+
+void
+foo ()
+{
+  T t;
+  (void)t.a;
+  (void)t.b;
+  (void)t.f();
+  (void)t.g();
+  (void)t.v();
+  (void)t.S::v();
+
+  U u;
+  (void)u.T::a;
+  (void)u.b;
+  (void)u.T::f();
+  (void)u.g();
+  (void)u.v();
+  (void)u.T::v();
+  (void)((T&)u).S::v();
+}
+
+T *x;
+template <int N>
+__attribute__((noinline, noclone)) int
+bar (T *p, int q)
+{
+  switch (q)
+    {
+    // These shouldn't fail:
+    case 0x10:
+    case 0x20:
+    case 0x30:
+    case 0x40:
+      {
+       T &r = *p;
+       break;
+      }
+    case 0x21:
+    case 0x31:
+      return p->b;
+    case 0x22:
+    case 0x32:
+      return p->g ();
+    case 0x23:
+    case 0x33:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    case 0x44:
+      return reinterpret_cast<U*>(p)->v() - 2;
+    // These should:
+    case 0x11:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-3.C:75:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x12:
+      return p->g ();
+    // { dg-output "\[^\n\r]*vptr-3.C:82:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x13:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    // { dg-output "\[^\n\r]*vptr-3.C:89:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x34:
+      return reinterpret_cast<U*>(p)->v() - 2;
+    // { dg-output "\[^\n\r]*vptr-3.C:97:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'U'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 16 within object of type 'U'(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 8 within object of type 'U'(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^                                                 ~~~~~~~~~~~~~~~~~~~~~~~(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "                                                                vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "              \\^                        ~~~~~~~~~~~(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "                                       vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    case 0x41:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-3.C:107:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x42:
+      return p->g ();
+    // { dg-output "\[^\n\r]*vptr-3.C:114:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x43:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    // { dg-output "\[^\n\r]*vptr-3.C:121:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x51:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-3.C:129:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object has invalid vptr(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  00 00 00 00 00 00 00 00  \[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "              \\^~~~~~~~~~~~~~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "  ?.. .. .. ..  ?00 00 00 00  ?.. .. .. ..  ?\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "              invalid vptr" }
+    }
+  return 0;
+}
+
+char b[sizeof (U)] __attribute__((aligned (__alignof__ (U)))) = {};
+
+__attribute__((noinline, noclone)) void
+baz (int q)
+{
+  T *p = 0;
+  S *s = 0;
+  U *u = 0;
+  switch (q)
+    {
+    case 0x10: case 0x11: case 0x12: case 0x13:
+      s = new S;
+      bar<0> (reinterpret_cast<T *>(s), q);
+      delete s;
+      break;
+    case 0x20: case 0x21: case 0x22: case 0x23:
+      p = new T;
+      bar<0> (p, q);
+      delete p;
+      break;
+    case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+      u = new U;
+      bar<0> (u, q);
+      delete u;
+      break;
+    case 0x40: case 0x41: case 0x42: case 0x43: case 0x44:
+      u = new U;
+      bar<0> (reinterpret_cast<T *>(u), q);
+      delete u;
+      break;
+    case 0x51:
+      p = reinterpret_cast<T*>(b);
+      bar<0> (p, q);
+      break;
+    }
+}
+
+int
+main ()
+{
+  foo ();
+  for (int q = 0; q < 0x52; q++)
+    baz (q);
+}
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-4.C b/gcc/testsuite/g++.dg/ubsan/vptr-4.C
new file mode 100644 (file)
index 0000000..1c037d0
--- /dev/null
@@ -0,0 +1,54 @@
+// Verify that -fsanitize=vptr downcast instrumentation works properly
+// inside of constexpr.
+// { dg-do compile }
+// { dg-options "-std=c++11 -fsanitize=vptr" }
+
+struct S {
+  constexpr S() : a(0) {}
+  int a;
+  int f() { return 0; }
+  virtual int v() { return 0; }
+};
+
+struct T : S {
+  constexpr T() : b(0) {}
+  int b;
+  int g() { return 0; }
+  virtual int v() { return 1; }
+  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); }
+};
+
+constexpr T t;
+constexpr const T *p = t.foo ();
+
+template <typename U>
+struct V {
+  constexpr V() : a(0) {}
+  int a;
+  int f() { return 0; }
+  virtual int v() { return 0; }
+};
+
+template <typename U>
+struct W : V<U> {
+  constexpr W() : b(0) {}
+  int b;
+  int g() { return 0; }
+  virtual int v() { return 1; }
+  constexpr const W<U> *foo() { return (const W<U> *) reinterpret_cast<const V<U> *> (this); }
+};
+
+constexpr W<int> w;
+constexpr const W<int> *s = w.foo ();
+
+template <typename U>
+int foo (void)
+{
+  static constexpr T t;
+  static constexpr const T *p = t.foo ();
+  static constexpr W<U> w;
+  static constexpr const W<U> *s = w.foo ();
+  return t.b + w.b;
+}
+
+int x = foo <char> ();
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-5.C b/gcc/testsuite/g++.dg/ubsan/vptr-5.C
new file mode 100644 (file)
index 0000000..fb9d15c
--- /dev/null
@@ -0,0 +1,32 @@
+// { dg-do run }
+// { dg-options "-fsanitize=vptr" }
+
+struct S
+{
+  S() : a(0) {}
+  ~S() {}
+  int a;
+  int f() { return 0; }
+  virtual int v() { return 0; }
+};
+
+struct T : S
+{
+  T() : b(0) {}
+  int b;
+  int g() { return 0; }
+  virtual int v() { return 1; }
+};
+
+T *
+foo (S *p)
+{
+  return (T *) p;
+}
+
+int
+main ()
+{
+  if (foo (__null) != __null)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-6.C b/gcc/testsuite/g++.dg/ubsan/vptr-6.C
new file mode 100644 (file)
index 0000000..1e595c5
--- /dev/null
@@ -0,0 +1,32 @@
+// { dg-do compile }
+// { dg-skip-if "" { *-*-* } { "-flto" } { "" } }
+// { dg-options "-fsanitize=vptr -O2 -fdump-tree-optimized" }
+
+struct S { virtual ~S (); int i; _Complex int j[5]; };
+
+int
+f1 (S *p)
+{
+  return p->i;
+}
+
+int
+f2 (S *p)
+{
+  return *&p->i;
+}
+
+_Complex int *
+f3 (S *p, S *q)
+{
+  return &p->j[q->i];
+}
+
+int
+f4 (S &p, S &q)
+{
+  return __imag__ p.j[q.i];
+}
+
+// { dg-final { scan-tree-dump-times "__ubsan_handle_dynamic_type_cache_miss" 5 "optimized" } }
+// { dg-final { cleanup-tree-dump "optimized" } }
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-7.C b/gcc/testsuite/g++.dg/ubsan/vptr-7.C
new file mode 100644 (file)
index 0000000..d3ff1a3
--- /dev/null
@@ -0,0 +1,26 @@
+// { dg-do compile }
+// { dg-skip-if "" { *-*-* } { "-flto" } { "" } }
+// { dg-options "-fsanitize=vptr -O2 -fdump-tree-optimized" }
+
+struct S { virtual ~S (); int i; };
+
+int *
+f1 (S *p)
+{
+  return &p->i;
+}
+
+int *
+f2 (S *p)
+{
+  return &*&p->i;
+}
+
+int &
+f3 (S *p)
+{
+  return p->i;
+}
+
+// { dg-final { scan-tree-dump-times "__ubsan_handle_dynamic_type_cache_miss" 0 "optimized" } }
+// { dg-final { cleanup-tree-dump "optimized" } }
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-8.C b/gcc/testsuite/g++.dg/ubsan/vptr-8.C
new file mode 100644 (file)
index 0000000..1533f20
--- /dev/null
@@ -0,0 +1,32 @@
+// { dg-do run }
+// { dg-shouldfail "ubsan" }
+// { dg-options "-fsanitize=vptr -fno-sanitize-recover=vptr" }
+
+extern "C" void abort ();
+
+struct S { virtual void f () {} };
+struct T : S { ~T (); };
+struct U : S { };
+struct V : T, virtual U {};
+
+U *up;
+V *vp;
+
+int
+main ()
+{
+  V v;
+  up = vp = &v;
+}
+
+T::~T ()
+{
+  if (vp != up)
+   abort ();
+}
+
+// { dg-output "\[^\n\r]*vptr-8.C:24:\[0-9]*: runtime error: cast to virtual base of address 0x\[0-9a-fA-F]* which does not point to an object of type 'V'(\n|\r\n|\r)" }
+// { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'T'(\n|\r\n|\r)" }
+// { dg-output "  ?.. .. .. ..  ?.. .. .. ..  ?.. .. .. .. \[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output "              ?\\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output "              ?vptr for 'T'\[^\n\r]*(\n|\r\n|\r)" }
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-9.C b/gcc/testsuite/g++.dg/ubsan/vptr-9.C
new file mode 100644 (file)
index 0000000..482e3e6
--- /dev/null
@@ -0,0 +1,22 @@
+// { dg-do run }
+// { dg-shouldfail "ubsan" }
+// { dg-options "-fsanitize=vptr -fno-sanitize-recover=undefined" }
+
+struct S { virtual int f () { return 0; } };
+struct T : virtual S {};
+struct U { virtual int f () { return 0; } };
+
+int
+main ()
+{
+  U u;
+  T *t = (T *) &u;
+  S *s = t;
+  return s->f ();
+}
+
+// { dg-output "\[^\n\r]*vptr-9.C:14:\[0-9]*: runtime error: cast to virtual base of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+// { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+// { dg-output "  ?.. .. .. ..  ?.. .. .. ..  ?.. .. .. .. \[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output "              ?\\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output "              ?vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
index 31db17a7dbfe7cf3b42da8dd0ea45c391d4c88db..fa6caef21e185cac61cdbea24394a62ed21c0524 100644 (file)
@@ -1929,6 +1929,22 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
   if (gimple_call_flags (call)
       & (ECF_PURE|ECF_CONST|ECF_LOOPING_CONST_OR_PURE|ECF_NOVOPS))
     return false;
+  if (gimple_call_internal_p (call))
+    switch (gimple_call_internal_fn (call))
+      {
+       /* Treat these internal calls like ECF_PURE for aliasing,
+          they don't write to any memory the program should care about.
+          They have important other side-effects, and read memory,
+          so can't be ECF_NOVOPS.  */
+      case IFN_UBSAN_NULL:
+      case IFN_UBSAN_BOUNDS:
+      case IFN_UBSAN_VPTR:
+      case IFN_UBSAN_OBJECT_SIZE:
+      case IFN_ASAN_CHECK:
+       return false;
+      default:
+       break;
+      }
 
   base = ao_ref_base (ref);
   if (!base)
index 7c94dab036c0f8bbb9792729038cc8c00f2c70fa..5bf53276d7dc0a11888d184868832cfcd019861f 100644 (file)
@@ -981,6 +981,167 @@ ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi)
   return gsi_end_p (*gsi);
 }
 
+/* Cached __ubsan_vptr_type_cache decl.  */
+static GTY(()) tree ubsan_vptr_type_cache_decl;
+
+/* Expand UBSAN_VPTR 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.  */
+
+bool
+ubsan_expand_vptr_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) == 5);
+  tree op = gimple_call_arg (stmt, 0);
+  tree vptr = gimple_call_arg (stmt, 1);
+  tree str_hash = gimple_call_arg (stmt, 2);
+  tree ti_decl_addr = gimple_call_arg (stmt, 3);
+  tree ckind_tree = gimple_call_arg (stmt, 4);
+  ubsan_null_ckind ckind = (ubsan_null_ckind) tree_to_uhwi (ckind_tree);
+  tree type = TREE_TYPE (TREE_TYPE (ckind_tree));
+  gimple g;
+  basic_block fallthru_bb = NULL;
+
+  if (ckind == UBSAN_DOWNCAST_POINTER)
+    {
+      /* Guard everything with if (op != NULL) { ... }.  */
+      basic_block then_bb;
+      gimple_stmt_iterator cond_insert_point
+       = create_cond_insert_point (gsip, false, false, true,
+                                   &then_bb, &fallthru_bb);
+      g = gimple_build_cond (NE_EXPR, op, build_zero_cst (TREE_TYPE (op)),
+                            NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+      *gsip = gsi_after_labels (then_bb);
+      gsi_remove (&gsi, false);
+      gsi_insert_before (gsip, stmt, GSI_NEW_STMT);
+      gsi = *gsip;
+    }
+
+  tree htype = TREE_TYPE (str_hash);
+  tree cst = wide_int_to_tree (htype,
+                              wi::uhwi (((uint64_t) 0x9ddfea08 << 32)
+                              | 0xeb382d69, 64));
+  g = gimple_build_assign (make_ssa_name (htype), BIT_XOR_EXPR,
+                          vptr, str_hash);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  g = gimple_build_assign (make_ssa_name (htype), MULT_EXPR,
+                          gimple_assign_lhs (g), cst);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  tree t1 = gimple_assign_lhs (g);
+  g = gimple_build_assign (make_ssa_name (htype), LSHIFT_EXPR,
+                          t1, build_int_cst (integer_type_node, 47));
+  gimple_set_location (g, loc);
+  tree t2 = gimple_assign_lhs (g);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  g = gimple_build_assign (make_ssa_name (htype), BIT_XOR_EXPR,
+                          vptr, t1);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  g = gimple_build_assign (make_ssa_name (htype), BIT_XOR_EXPR,
+                          t2, gimple_assign_lhs (g));
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  g = gimple_build_assign (make_ssa_name (htype), MULT_EXPR,
+                          gimple_assign_lhs (g), cst);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  tree t3 = gimple_assign_lhs (g);
+  g = gimple_build_assign (make_ssa_name (htype), LSHIFT_EXPR,
+                          t3, build_int_cst (integer_type_node, 47));
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  g = gimple_build_assign (make_ssa_name (htype), BIT_XOR_EXPR,
+                          t3, gimple_assign_lhs (g));
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  g = gimple_build_assign (make_ssa_name (htype), MULT_EXPR,
+                          gimple_assign_lhs (g), cst);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+  if (!useless_type_conversion_p (pointer_sized_int_node, htype))
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+                              NOP_EXPR, gimple_assign_lhs (g));
+      gimple_set_location (g, loc);
+      gsi_insert_before (gsip, g, GSI_SAME_STMT);
+    }
+  tree hash = gimple_assign_lhs (g);
+
+  if (ubsan_vptr_type_cache_decl == NULL_TREE)
+    {
+      tree atype = build_array_type_nelts (pointer_sized_int_node, 128);
+      tree array = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+                              get_identifier ("__ubsan_vptr_type_cache"),
+                              atype);
+      DECL_ARTIFICIAL (array) = 1;
+      DECL_IGNORED_P (array) = 1;
+      TREE_PUBLIC (array) = 1;
+      TREE_STATIC (array) = 1;
+      DECL_EXTERNAL (array) = 1;
+      DECL_VISIBILITY (array) = VISIBILITY_DEFAULT;
+      DECL_VISIBILITY_SPECIFIED (array) = 1;
+      varpool_node::finalize_decl (array);
+      ubsan_vptr_type_cache_decl = array;
+   }
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+                          BIT_AND_EXPR, hash,
+                          build_int_cst (pointer_sized_int_node, 127));
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+
+  tree c = build4_loc (loc, ARRAY_REF, pointer_sized_int_node,
+                      ubsan_vptr_type_cache_decl, gimple_assign_lhs (g),
+                      NULL_TREE, NULL_TREE);
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+                          ARRAY_REF, c);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+
+  basic_block then_bb, fallthru2_bb;
+  gimple_stmt_iterator cond_insert_point
+    = create_cond_insert_point (gsip, false, false, true,
+                               &then_bb, &fallthru2_bb);
+  g = gimple_build_cond (NE_EXPR, gimple_assign_lhs (g), hash,
+                        NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+  gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+  *gsip = gsi_after_labels (then_bb);
+  if (fallthru_bb == NULL)
+    fallthru_bb = fallthru2_bb;
+
+  tree data
+    = ubsan_create_data ("__ubsan_vptr_data", 1, &loc,
+                        ubsan_type_descriptor (type), NULL_TREE, ti_decl_addr,
+                        build_int_cst (unsigned_char_type_node, ckind),
+                        NULL_TREE);
+  data = build_fold_addr_expr_loc (loc, data);
+  enum built_in_function bcode
+    = (flag_sanitize_recover & SANITIZE_VPTR)
+      ? BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS
+      : BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT;
+
+  g = gimple_build_call (builtin_decl_explicit (bcode), 3, data, op, hash);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsip, g, GSI_SAME_STMT);
+
+  /* Point GSI to next logical statement.  */
+  *gsip = gsi_start_bb (fallthru_bb);
+
+  /* Get rid of the UBSAN_VPTR call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi, true);
+  return gsi_end_p (*gsip);
+}
+
 /* 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.  */
 
index 9b8a59b6c84d84096113afb5a260ed50b8a8fd24..9f23a382ec08b079937526b23af3dc8ea23990d8 100644 (file)
@@ -28,7 +28,11 @@ enum ubsan_null_ckind {
   UBSAN_REF_BINDING,
   UBSAN_MEMBER_ACCESS,
   UBSAN_MEMBER_CALL,
-  UBSAN_CTOR_CALL
+  UBSAN_CTOR_CALL,
+  UBSAN_DOWNCAST_POINTER,
+  UBSAN_DOWNCAST_REFERENCE,
+  UBSAN_UPCAST,
+  UBSAN_CAST_TO_VBASE
 };
 
 /* This controls how ubsan prints types.  Used in ubsan_type_descriptor.  */
@@ -42,6 +46,7 @@ extern bool do_ubsan_in_current_function (void);
 extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
 extern bool ubsan_expand_null_ifn (gimple_stmt_iterator *);
 extern bool ubsan_expand_objsize_ifn (gimple_stmt_iterator *);
+extern bool ubsan_expand_vptr_ifn (gimple_stmt_iterator *);
 extern bool ubsan_instrument_unreachable (gimple_stmt_iterator *);
 extern tree ubsan_create_data (const char *, int, const location_t *, ...);
 extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);