public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [RFC PATCH] -fsanitize=vptr instrumentation
@ 2014-09-16 14:56 Jakub Jelinek
  2014-09-17 14:27 ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-09-16 14:56 UTC (permalink / raw)
  To: Jason Merrill, Marek Polacek; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 50344 bytes --]

Hi!

This patch attempts to instrument C++ when a pointer might not point
to object of the right dynamic type.

clang++ apparently instruments:
1) member accesses
2) method calls
3) downcasts (both pointer and reference downcasts)

The vptr-1.C (and from there derived -2 and -3 testcases) attempts to
model compiler-rt ubsan/TestCases/TypeCheck/vptr.cpp test.
1) and 2) are instrumented during genericization, 3) is instrumented
much earlier (build_static_cast_1), because afterwards it is not possible
to differentiate between reinterpret_cast, static_cast etc., but so that
it works with constexpr it is expanded only partially (just a builtin
function call) and the rest of instrumentation around it is added during
genericization; the builtin is ignored during constexpr evaluation.

The instrumentation works by computing a hash from the mangled type name
of the type we expect, and vtable pointer value in the object, this hash
is first used for a quick check in __ubsan_vptr_type_cache 128 element
hash table (if (__ubsan_vptr_type_cache[hash & 127] == hash), we don't
call anything and assume the access is fine), otherwise we call a library
function with the hash value, vtable pointer and rtti info what we are
looking for.

I've tried to optimize away checks for the most simple cases,
like when a member function is called on an object, not through a function
pointer (because then we know the vptr and the compiler should check that at
compile time), or when doing member access through this or member calls
through this (but only if it is to the actual type of the current class);
the this optimizations aren't done in ctors because the vptr is changing
there and the caller doesn't check for obvious reasons the vptr pointer
before calling ctor.

Attached are 3 testcases I'd like to discuss:

vptr-5.C is one Jason mailed me yesterday, clang++ doesn't instrument this
and g++ right now doesn't either, build_static_cast_1 certainly isn't called
in that case, and I must say I have no idea what should be checked there,
where etc.

vptr-6.C shows where the this optimization is performed and where it isn't
(clang++ has 10 instrumentations in T::h and 1 in S::l, g++ has fewer than
that, but not 0 in T::h (1 in S::l is right and needed I think)).

For downcasts, I'm testing the downcasted pointer for non-NULL (clang++ does
too), but not so for member access.  vptr-8.C testcase is me trying to
understand what is and what is not considered valid C++ for member accesses.
clang++ through -fsanitize=null already complains in f[1-6], g++ does
not (it will only instrument for null actual accesses, not mere taking of
address), but with -fsanitize=undefined (what matters is null,vptr)
clang++ complains in f1, complains in f2 and then segfaults on the vptr
instrumentation trying to read vtable pointer.  g++ doesn't complain on f1,
but complains on f2 and segfaults there too.
I guess my question is what exactly is strictly valid code (offsetof
really shouldn't be written that way I believe), and as f6 testcase shows,
often the pointer even isn't NULL when already taking address of a field
in dereference of a NULL pointer.
I hope all of f[1-6] is invalid, I really don't see how we could instrument
member accesses otherwise (we'd need to limit to not taking address of it);
NULL pointer shouldn't point at a valid object.

Thoughts, comments, suggestions for how to improve this?

2014-09-16  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
	and UBSAN_DOWNCAST_REFERENCE.
cp/
	* config-lang.in (gtfiles): Add cp/cp-ubsan.c.
	* cp-gimplify.c (cp_genericize_r): Call
	cp_ubsan_maybe_instrument_member_call for member calls,
	cp_ubsan_maybe_instrument_member_access for member accesses
	and cp_ubsan_fixup_downcast_instrumentation for downcast
	instrumentation.
	* cp-tree.h (cp_ubsan_maybe_instrument_member_call,
	cp_ubsan_maybe_instrument_member_access,
	cp_ubsan_maybe_instrument_downcast,
	cp_ubsan_fixup_downcast_instrumentation): New prototypes.
	* cp-ubsan.c: New file.
	* Make-lang.in (CXX_AND_OBJCXX_OBJS): Add cp/cp-ubsan.o.
	* semantics.c (cxx_eval_call_expression): Handle 4 argument
	BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS{,_ABORT}.
	* typeck.c (build_class_member_access_expr): Provide locus
	for COMPONENT_REFs.
	(build_static_cast_1): Instrument downcasts.
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.

--- gcc/opts.c.jj	2014-09-10 21:57:10.031321254 +0200
+++ gcc/opts.c	2014-09-16 10:02:49.469868881 +0200
@@ -1504,6 +1504,7 @@ common_handle_option (struct gcc_options
 	      { "returns-nonnull-attribute",
 		SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
 		sizeof "returns-nonnull-attribute" - 1 },
+	      { "vptr", SANITIZE_VPTR, sizeof "vptr" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
--- gcc/ubsan.h.jj	2014-09-10 21:57:10.020321310 +0200
+++ gcc/ubsan.h	2014-09-16 10:02:49.491868789 +0200
@@ -28,7 +28,9 @@ 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
 };
 
 /* This controls how ubsan prints types.  Used in ubsan_type_descriptor.  */
--- gcc/sanitizer.def.jj	2014-09-10 21:57:10.021321305 +0200
+++ gcc/sanitizer.def	2014-09-16 10:02:49.502868815 +0200
@@ -433,3 +433,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN
 		      "__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_NORETURN_NOTHROW_LEAF_LIST)
--- gcc/cp/cp-tree.h.jj	2014-09-16 10:00:35.094564549 +0200
+++ gcc/cp/cp-tree.h	2014-09-16 10:02:49.504868790 +0200
@@ -6298,6 +6298,12 @@ bool cilkplus_an_triplet_types_ok_p
 /* 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_maybe_instrument_member_access (tree);
+extern tree cp_ubsan_maybe_instrument_downcast	(location_t, tree, tree);
+extern void cp_ubsan_fixup_downcast_instrumentation (tree *);
+
 /* -- end of C++ */
 
 #endif /* ! GCC_CP_TREE_H */
--- gcc/cp/cp-gimplify.c.jj	2014-09-10 21:57:10.199320386 +0200
+++ gcc/cp/cp-gimplify.c	2014-09-16 10:02:49.513868732 +0200
@@ -1197,9 +1197,11 @@ cp_genericize_r (tree *stmt_p, int *walk
 	*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,9 +1216,31 @@ cp_genericize_r (tree *stmt_p, int *walk
 		= 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);
+	    }
+	  else if (flag_sanitize & SANITIZE_VPTR)
+	    {
+	      tree fndecl = get_callee_fndecl (stmt);
+	      if (fndecl
+		  && DECL_BUILT_IN (fndecl)
+		  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+		switch (DECL_FUNCTION_CODE (fndecl))
+		  {
+		  case BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS:
+		  case BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT:
+		    cp_ubsan_fixup_downcast_instrumentation (stmt_p);
+		    break;
+		  default:
+		    break;
+		  }
 	    }
 	}
+      else if ((flag_sanitize & SANITIZE_VPTR)
+	       && TREE_CODE (stmt) == COMPONENT_REF)
+	cp_ubsan_maybe_instrument_member_access (stmt);
     }
 
   p_set->add (*stmt_p);
--- gcc/cp/semantics.c.jj	2014-09-10 11:13:32.000000000 +0200
+++ gcc/cp/semantics.c	2014-09-16 11:52:53.780723771 +0200
@@ -8553,8 +8553,20 @@ cxx_eval_call_expression (const constexp
   if (DECL_CLONED_FUNCTION_P (fun))
     fun = DECL_CLONED_FUNCTION (fun);
   if (is_builtin_fn (fun))
-    return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
-					   addr, non_constant_p, overflow_p);
+    {
+      /* Ignore -fsanitize=vptr instrumentation.  */
+      if ((flag_sanitize & SANITIZE_VPTR)
+	  && DECL_BUILT_IN_CLASS (fun) == BUILT_IN_NORMAL
+	  && (DECL_FUNCTION_CODE (fun)
+	      == (flag_sanitize_recover
+		  ? BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS
+		  : BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT))
+	  && call_expr_nargs (t) == 4)
+	return void_node;
+
+      return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
+					     addr, non_constant_p, overflow_p);
+    }
   if (!DECL_DECLARED_CONSTEXPR_P (fun))
     {
       if (!allow_non_constant)
--- gcc/cp/Make-lang.in.jj	2014-09-15 20:19:15.931342604 +0200
+++ gcc/cp/Make-lang.in	2014-09-16 10:02:49.513868732 +0200
@@ -78,7 +78,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.
  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 $(CXX_C_OBJS)
+ cp/vtable-class-hierarchy.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)
--- gcc/cp/cp-ubsan.c.jj	2014-09-16 10:02:49.514868725 +0200
+++ gcc/cp/cp-ubsan.c	2014-09-16 15:16:40.612132021 +0200
@@ -0,0 +1,308 @@
+/* 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 "tree.h"
+#include "alloc-pool.h"
+#include "cgraph.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"
+
+/* Cached __ubsan_vptr_type_cache decl.  */
+static GTY(()) tree ubsan_vptr_type_cache_decl;
+
+/* Emit if (__ubsan_vptr_type_cache[hash & 127] != hash) or
+   if (op && __ubsan_vptr_type_cache[hash & 127] != hash) test around
+   call.  */
+
+static tree
+cp_ubsan_cache_test (location_t loc, tree op, tree hash, tree call,
+		     enum ubsan_null_ckind ckind)
+{
+  tree t = fold_build2_loc (loc, BIT_AND_EXPR, pointer_sized_int_node, hash,
+			    build_int_cst (pointer_sized_int_node, 127));
+  t = build4_loc (loc, ARRAY_REF, pointer_sized_int_node,
+		     ubsan_vptr_type_cache_decl, t, NULL_TREE, NULL_TREE);
+  tree cond = fold_build2_loc (loc, NE_EXPR, boolean_type_node, t, hash);
+  TREE_SIDE_EFFECTS (cond) = 1;
+  if (ckind == UBSAN_DOWNCAST_POINTER)
+    cond = fold_build2_loc (loc, TRUTH_ANDIF_EXPR, boolean_type_node,
+			    fold_build2_loc (loc, NE_EXPR, boolean_type_node,
+					     op,
+					     build_zero_cst (TREE_TYPE (op))),
+			    cond);
+  return fold_build3_loc (loc, COND_EXPR, void_type_node, cond, call,
+			  void_node);
+}
+
+/* Helper function for
+   cp_ubsan_maybe_instrument_{member_{call,access},downcast}.  */
+
+static tree
+cp_ubsan_maybe_instrument_vptr (location_t loc, tree op, tree type,
+				bool is_addr, enum ubsan_null_ckind ckind)
+{
+  if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
+    return NULL_TREE;
+
+  if (current_function_decl == NULL_TREE
+      || lookup_attribute ("no_sanitize_undefined",
+			   DECL_ATTRIBUTES (current_function_decl)))
+    return NULL_TREE;
+
+  type = TYPE_MAIN_VARIANT (type);
+  if (!CLASS_TYPE_P (type) || !CLASSTYPE_VTABLES (type))
+    return NULL_TREE;
+
+  /* T t; t.foo (); doesn't need instrumentation, if the type is known.  */
+  if (is_addr
+      && TREE_CODE (op) == ADDR_EXPR
+      && DECL_P (TREE_OPERAND (op, 0))
+      && same_type_p (type,
+		      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (op, 0)))))
+    return NULL_TREE;
+
+  if (current_function_decl
+      && TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE
+      && !DECL_CONSTRUCTOR_P (current_function_decl))
+    {
+      tree ptr = NULL_TREE;
+      if (is_addr)
+	ptr = op;
+      else if (TREE_CODE (op) == INDIRECT_REF)
+	ptr = TREE_OPERAND (op, 0);
+      if (ptr)
+	{
+	  /* Don't instrument this->member or this->method () in methods,
+	     the virtual table pointer should have been verified before
+	     calling the method by the caller.  */
+	  STRIP_NOPS (ptr);
+	  if (TREE_CODE (ptr) == PARM_DECL
+	      && DECL_ARTIFICIAL (ptr)
+	      && DECL_NAME (ptr) == this_identifier
+	      && DECL_CONTEXT (ptr) == current_function_decl
+	      && POINTER_TYPE_P (TREE_TYPE (ptr))
+	      && same_type_p (type,
+			      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (ptr)))))
+	    return NULL_TREE;
+	}
+    }
+
+  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);
+  TREE_SIDE_EFFECTS (vptr) = 1;
+  vptr = fold_convert_loc (loc, pointer_sized_int_node, vptr);
+  vptr = fold_convert_loc (loc, uint64_type_node, vptr);
+  vptr = build1_loc (loc, SAVE_EXPR, uint64_type_node, vptr);
+  TREE_SIDE_EFFECTS (vptr) = 1;
+  /* Hash in 2 different hashes of mangled type name with the value of
+     vptr pointer.  */
+  tree cst = wide_int_to_tree (uint64_type_node,
+			       wi::uhwi (((uint64_t) 0x9ddfea08 << 32)
+					 | 0xeb382d69, 64));
+  tree t1 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, 
+			     str_hash, vptr);
+  t1 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t1, cst);
+  t1 = build1_loc (loc, SAVE_EXPR, uint64_type_node, t1);
+  TREE_SIDE_EFFECTS (t1) = 1;
+  tree t2 = fold_build2_loc (loc, LSHIFT_EXPR, uint64_type_node,
+			     t1, build_int_cst (integer_type_node, 47));
+  t2 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, t2, t1);
+  t2 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, vptr, t2);
+  t2 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t2, cst);
+  t2 = build1_loc (loc, SAVE_EXPR, uint64_type_node, t2);
+  TREE_SIDE_EFFECTS (t2) = 1;
+  tree t3 = fold_build2_loc (loc, LSHIFT_EXPR, uint64_type_node,
+			     t2, build_int_cst (integer_type_node, 47));
+  t3 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, t3, t2);
+  t3 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t3, cst);
+  tree hash = fold_convert_loc (loc, pointer_sized_int_node, t3);
+  hash = build1_loc (loc, SAVE_EXPR, pointer_sized_int_node, hash);
+  TREE_SIDE_EFFECTS (hash) = 1;
+  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_STATIC (array) = 1;
+      DECL_EXTERNAL (array) = 1;
+      layout_decl (array, 0);
+      ubsan_vptr_type_cache_decl = array;
+    }
+  tree data
+    = ubsan_create_data ("__ubsan_vptr_data", 1, &loc,
+			 ubsan_type_descriptor (type), NULL_TREE,
+			 build_address (get_tinfo_decl (type)),
+			 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
+      ? BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS
+      : BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT;
+  tree call = builtin_decl_explicit (bcode), ret;
+  /* As UBSAN_DOWNCAST_* is called from build_static_cast_1, emit there
+     only the call and add the guard when genericizing the call.  */
+  if (ckind != UBSAN_DOWNCAST_POINTER && ckind != UBSAN_DOWNCAST_REFERENCE)
+    {
+      call = build_call_expr_loc (loc, call, 3, data, op,
+				  ubsan_encode_value (hash));
+      TREE_SIDE_EFFECTS (call) = 1;
+      ret = cp_ubsan_cache_test (loc, op, hash, call, ckind);
+    }
+  else
+    {
+      call = build_call_expr_loc (loc, call, 4, data, op,
+				  ubsan_encode_value (hash),
+				  build_int_cst (integer_type_node, ckind));
+      TREE_SIDE_EFFECTS (call) = 1;
+      ret = call;
+    }
+  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), ret, op);
+}
+
+/* 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;
+}
+
+/* Instrument a member access.  */
+
+void
+cp_ubsan_maybe_instrument_member_access (tree stmt)
+{
+  if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1)))
+    return;
+
+  tree base = TREE_OPERAND (stmt, 0);
+  if (TREE_CODE (base) == COMPONENT_REF
+      && DECL_ARTIFICIAL (TREE_OPERAND (base, 1)))
+    {
+      tree base2 = TREE_OPERAND (base, 0);
+      while (TREE_CODE (base2) == COMPONENT_REF
+	     || TREE_CODE (base2) == ARRAY_REF
+	     || TREE_CODE (base2) == ARRAY_RANGE_REF)
+	base2 = TREE_OPERAND (base2, 0);
+      if (TREE_CODE (base2) != INDIRECT_REF
+	  && TREE_CODE (base2) != MEM_REF)
+	return;
+    }
+  else if (TREE_CODE (base) != INDIRECT_REF
+	   && TREE_CODE (base) != MEM_REF)
+    return;
+
+  base = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), base,
+					 TREE_TYPE (base), false,
+					 UBSAN_MEMBER_ACCESS);
+  if (base)
+    TREE_OPERAND (stmt, 0)
+      = build_fold_indirect_ref_loc (EXPR_LOCATION (stmt), base);
+}
+
+/* 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);
+}
+
+/* Fix up downcast instrumentation.  */
+
+void
+cp_ubsan_fixup_downcast_instrumentation (tree *stmt_p)
+{
+  tree stmt = *stmt_p;
+  if (call_expr_nargs (stmt) != 4)
+    return;
+
+  location_t loc = EXPR_LOCATION (stmt);
+  tree data = CALL_EXPR_ARG (stmt, 0);
+  tree op = CALL_EXPR_ARG (stmt, 1);
+  tree hash = CALL_EXPR_ARG (stmt, 2);
+  enum ubsan_null_ckind ckind
+    = (enum ubsan_null_ckind) tree_to_shwi (CALL_EXPR_ARG (stmt, 3));
+  enum built_in_function bcode
+    = flag_sanitize_recover
+      ? BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS
+      : BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT;
+  tree call = builtin_decl_explicit (bcode);
+  call = build_call_expr_loc (loc, call, 3, data, op,
+			      ubsan_encode_value (hash));
+  TREE_SIDE_EFFECTS (call) = 1;
+  *stmt_p = cp_ubsan_cache_test (loc, op, hash, call, ckind);
+}
+
+#include "gt-cp-cp-ubsan.h"
--- gcc/cp/typeck.c.jj	2014-09-10 21:57:10.078321013 +0200
+++ gcc/cp/typeck.c	2014-09-16 10:02:49.531868633 +0200
@@ -2414,8 +2414,8 @@ build_class_member_access_expr (tree obj
 	  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
@@ -6452,11 +6452,21 @@ build_static_cast_1 (tree type, tree exp
       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++.
 
@@ -6584,7 +6594,16 @@ build_static_cast_1 (tree type, tree exp
 			  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))
--- gcc/cp/config-lang.in.jj	2014-09-10 21:57:10.150320642 +0200
+++ gcc/cp/config-lang.in	2014-09-16 10:02:49.539868592 +0200
@@ -29,4 +29,4 @@ compilers="cc1plus\$(exeext)"
 
 target_libs="target-libstdc++-v3"
 
-gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c"
+gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c \$(srcdir)/cp/cp-ubsan.c"
--- gcc/flag-types.h.jj	2014-09-10 21:57:10.009321365 +0200
+++ gcc/flag-types.h	2014-09-16 10:02:49.549868539 +0200
@@ -236,12 +236,13 @@ enum sanitize_code {
   SANITIZE_ALIGNMENT = 1 << 17,
   SANITIZE_NONNULL_ATTRIBUTE = 1 << 18,
   SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1 << 19,
+  SANITIZE_VPTR = 1 << 20,
   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_RETURNS_NONNULL_ATTRIBUTE | SANITIZE_VPTR,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2014-09-16 12:48:54.852594440 +0200
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2014-09-16 14:46:16.156195978 +0200
@@ -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> ();
--- gcc/testsuite/g++.dg/ubsan/vptr-5.C.jj	2014-09-16 14:54:13.029814955 +0200
+++ gcc/testsuite/g++.dg/ubsan/vptr-5.C	2014-09-16 14:58:07.174642375 +0200
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-3.C.jj	2014-09-16 12:47:44.200954237 +0200
+++ gcc/testsuite/g++.dg/ubsan/vptr-3.C	2014-09-16 12:48:24.341762680 +0200
@@ -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);
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-2.C.jj	2014-09-16 12:06:08.451666897 +0200
+++ gcc/testsuite/g++.dg/ubsan/vptr-2.C	2014-09-16 12:47:31.986012489 +0200
@@ -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);
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-1.C.jj	2014-09-16 10:02:49.549868539 +0200
+++ gcc/testsuite/g++.dg/ubsan/vptr-1.C	2014-09-16 10:02:49.549868539 +0200
@@ -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);
+}

	Jakub

[-- Attachment #2: vptr-5.C --]
[-- Type: text/plain, Size: 3108 bytes --]

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();                                                                                                                                       
}                                                                                                                                                  

[-- Attachment #3: vptr-6.C --]
[-- Type: text/plain, Size: 398 bytes --]

struct T;

struct S
{
  S() : a(0) {}
  ~S() {}
  int a;
  int f() { return 0; }
  virtual int v() { return 0; }
  const T *l();
};

struct T : S
{
  T() : b(0) {}
  int b;
  int g() { return 0; }
  virtual int v() { return 1; }
  int h();
};

int
T::h()
{
  return a + b + this->a + this->b + f() + g() + v() + this->f() + this->g() + this->v();
}

const T *
S::l()
{
  return (const T *) this;
}

[-- Attachment #4: vptr-8.C --]
[-- Type: text/plain, Size: 665 bytes --]

struct R
{
  int r;
};

struct S
{
  S() : a(0) {}
  ~S() {}
  int a;
  int f() { return 0; }
  virtual int v() { return 0; }
};

struct T : R, S
{
  T() : b(0) {}
  int b;
  int g() { return 0; }
  virtual int v() { return 1; }
};

struct U
{
  char u;
  T t;
};

int *
f1 (R *p)
{
  return &p->r;
}

int *
f2 (S *p)
{
  return &p->a;
}

int *
f3 (T *p)
{
  return &p->a;
}

int *
f4 (T *p)
{
  return &p->b;
}

int *
f5 (U *p)
{
  return &p->t.b;
}

int *
f6 (U *p)
{
  T *t = &p->t;
  return &t->b;
}

int
main ()
{
  int *a = f1 (__null);
  int *b = f2 (__null);
  int *c = f3 (__null);
  int *d = f4 (__null);
  int *e = f5 (__null);
  int *f = f6 (__null);
}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] -fsanitize=vptr instrumentation
  2014-09-16 14:56 [RFC PATCH] -fsanitize=vptr instrumentation Jakub Jelinek
@ 2014-09-17 14:27 ` Jason Merrill
  2014-09-17 15:40   ` Jakub Jelinek
  2014-10-27 16:21   ` [PATCH] -fsanitize=vptr instrumentation (take 2) Jakub Jelinek
  0 siblings, 2 replies; 16+ messages in thread
From: Jason Merrill @ 2014-09-17 14:27 UTC (permalink / raw)
  To: Jakub Jelinek, Marek Polacek; +Cc: gcc-patches

On 09/16/2014 10:56 AM, Jakub Jelinek wrote:
> vptr-5.C is one Jason mailed me yesterday, clang++ doesn't instrument this
> and g++ right now doesn't either, build_static_cast_1 certainly isn't called
> in that case, and I must say I have no idea what should be checked there,
> where etc.

What needs to be checked is conversion (in this case implicit) to a 
virtual base; if the vptr doesn't point to a vtable that has the 
appropriate vbase offset, we should complain.

virtual base conversions are implemented in build_base_path under if 
(virtual_access).

> vptr-6.C shows where the this optimization is performed and where it isn't
> (clang++ has 10 instrumentations in T::h and 1 in S::l, g++ has fewer than
> that, but not 0 in T::h (1 in S::l is right and needed I think)).

I agree that 0 is enough for T::h and 1 for S::l.

> I hope all of f[1-6] is invalid, I really don't see how we could instrument
> member accesses otherwise (we'd need to limit to not taking address of it);
> NULL pointer shouldn't point at a valid object.

I don't see anything in the standard saying that these are undefined, 
only that trying to access the (non-)object pointed to is undefined.  It 
would be undefined if a conversion to virtual base were involved, i.e.

struct V: virtual R { };

// undefined if p doesn't point to a V because of the conversion to
// virtual base R
int* f7 (V* p) { return &p->r; }

These conditions were loosened in C++11 by DRs 597 and 1531; before that 
it was reasonable to regard f[1-6] as undefined, and perhaps clang is 
using the earlier interpretation.

> +  TREE_SIDE_EFFECTS (cond) = 1;
...
> +  TREE_SIDE_EFFECTS (hash) = 1;

Why do you need to set TREE_SIDE_EFFECTS on these?

> +  if (current_function_decl == NULL_TREE
> +      || lookup_attribute ("no_sanitize_undefined",
> +			   DECL_ATTRIBUTES (current_function_decl)))
> +    return NULL_TREE;

When would this be called outside a function?  If for namespace-scope 
variable initializers, I'd think we do want instrumentation.

> +  /* T t; t.foo (); doesn't need instrumentation, if the type is known.  */
> +  if (is_addr
> +      && TREE_CODE (op) == ADDR_EXPR
> +      && DECL_P (TREE_OPERAND (op, 0))
> +      && same_type_p (type,
> +		      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (op, 0)))))
> +    return NULL_TREE;

You might want to use resolves_to_fixed_type_p in the optimizations.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] -fsanitize=vptr instrumentation
  2014-09-17 14:27 ` Jason Merrill
@ 2014-09-17 15:40   ` Jakub Jelinek
  2014-09-17 18:02     ` Jason Merrill
  2014-10-27 16:21   ` [PATCH] -fsanitize=vptr instrumentation (take 2) Jakub Jelinek
  1 sibling, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-09-17 15:40 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Marek Polacek, gcc-patches

On Wed, Sep 17, 2014 at 10:27:02AM -0400, Jason Merrill wrote:
> On 09/16/2014 10:56 AM, Jakub Jelinek wrote:
> >vptr-5.C is one Jason mailed me yesterday, clang++ doesn't instrument this
> >and g++ right now doesn't either, build_static_cast_1 certainly isn't called
> >in that case, and I must say I have no idea what should be checked there,
> >where etc.
> 
> What needs to be checked is conversion (in this case implicit) to a virtual
> base; if the vptr doesn't point to a vtable that has the appropriate vbase
> offset, we should complain.
> 
> virtual base conversions are implemented in build_base_path under if
> (virtual_access).

build_base_path seems to be used in lots of places though, apparently
including member access, etc.  The ubsan library right now has just these
  const char *TypeCheckKinds[] = {                                                                                                                 
    "load of", "store to", "reference binding to", "member access within",                                                                         
    "member call on", "constructor call on", "downcast of", "downcast of"                                                                          
  };                                                                                                                                               
reasons for the runtime diagnostics (constructor call on, reference
binding to, load of and store to meant for other diagnostics), if what the
vptr-5.C testcase does for the pointer comparison? is not one of these, we'd
need to coordinate with upstream addition of other kinds (but,
build_base_path would need to be told what action it is, or it would need to
be instrumented in the callers of there like build_static_cast_1).
Suggestions on what the other kinds should be?

> >vptr-6.C shows where the this optimization is performed and where it isn't
> >(clang++ has 10 instrumentations in T::h and 1 in S::l, g++ has fewer than
> >that, but not 0 in T::h (1 in S::l is right and needed I think)).
> 
> I agree that 0 is enough for T::h and 1 for S::l.
> 
> >I hope all of f[1-6] is invalid, I really don't see how we could instrument
> >member accesses otherwise (we'd need to limit to not taking address of it);
> >NULL pointer shouldn't point at a valid object.
> 
> I don't see anything in the standard saying that these are undefined, only
> that trying to access the (non-)object pointed to is undefined.  It would be
> undefined if a conversion to virtual base were involved, i.e.
> 
> struct V: virtual R { };
> 
> // undefined if p doesn't point to a V because of the conversion to
> // virtual base R
> int* f7 (V* p) { return &p->r; }
> 
> These conditions were loosened in C++11 by DRs 597 and 1531; before that it
> was reasonable to regard f[1-6] as undefined, and perhaps clang is using the
> earlier interpretation.

:(.  Well, for NULL one could argue that it was never a pointer to an object
and never will be, but as it could be non-NULL pointer refering to an out of
life object (e.g. deleted), I guess we have to stop instrumenting any
"member accesses" if it is surrounded by ADDR_EXPR, right?
So, while we'll instrument
  T *p;
...
  int i = p->a;
we won't be able to instrument
  int *ip = &p->a;
  int i = *ip;

Based on the DR597 resolution, I guess the only cases -fsanitize=vptr
can instrument are those listed in those bullets there:
- the pointer is used to access a non-static data member or call a non-static member function of the object, or
- the pointer is implicitly converted (4.10 [conv.ptr]) to a pointer to a virtual base class type, or
- the pointer is used as the operand of a static_cast (5.2.9 [expr.static.cast]) except when the conversion is to pointer to cv void, or to pointer to cv void and subsequently to pointer to cv char or pointer to cv unsigned char, or
- the pointer is used as the operand of a dynamic_cast (5.2.7 [expr.dynamic.cast])...

the first bullet is supposedly instrumented in the patch (except we
instrument ADDR_EXPR we shouldn't), the second, is that what vptr-5.C
above is about?, the third one is partially the build_static_cast_1,
but we only instrument downcasts, shouldn't we instrument upcasts too,
or static_cast conversions to say POD types other than to
void/char pointers?
And for the last one, should we before dynamic_cast verify the object
passed to dynamic_cast has the expected vptr?

> >+  TREE_SIDE_EFFECTS (cond) = 1;
> ...
> >+  TREE_SIDE_EFFECTS (hash) = 1;
> 
> Why do you need to set TREE_SIDE_EFFECTS on these?

I guess I can try to remove those, at some point I had in the patch
TRUTH_AND_EXPR instead of TRUTH_ANDIF_EXPR for the downcasts, and
some TREE_SIDE_EFFECTS I've added until I've noticed the missing IF.

> >+  if (current_function_decl == NULL_TREE
> >+      || lookup_attribute ("no_sanitize_undefined",
> >+			   DECL_ATTRIBUTES (current_function_decl)))
> >+    return NULL_TREE;
> 
> When would this be called outside a function?  If for namespace-scope
> variable initializers, I'd think we do want instrumentation.

Ok.  Then one won't be able to avoid the instrumentation there, unless
using -fno-sanitize=vptr, but there is really no place to stick that on.
> 
> >+  /* T t; t.foo (); doesn't need instrumentation, if the type is known.  */
> >+  if (is_addr
> >+      && TREE_CODE (op) == ADDR_EXPR
> >+      && DECL_P (TREE_OPERAND (op, 0))
> >+      && same_type_p (type,
> >+		      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (op, 0)))))
> >+    return NULL_TREE;
> 
> You might want to use resolves_to_fixed_type_p in the optimizations.

Ok, will have a look at that function.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] -fsanitize=vptr instrumentation
  2014-09-17 15:40   ` Jakub Jelinek
@ 2014-09-17 18:02     ` Jason Merrill
  2014-09-17 20:42       ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2014-09-17 18:02 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, gcc-patches

On 09/17/2014 11:40 AM, Jakub Jelinek wrote:
> build_base_path seems to be used in lots of places though, apparently
> including member access, etc.  The ubsan library right now has just these
>    const char *TypeCheckKinds[] = {
>      "load of", "store to", "reference binding to", "member access within",
>      "member call on", "constructor call on", "downcast of", "downcast of"
>    };
> reasons for the runtime diagnostics (constructor call on, reference
> binding to, load of and store to meant for other diagnostics), if what the
> vptr-5.C testcase does for the pointer comparison? is not one of these, we'd
> need to coordinate with upstream addition of other kinds (but,
> build_base_path would need to be told what action it is, or it would need to
> be instrumented in the callers of there like build_static_cast_1).
> Suggestions on what the other kinds should be?

Well, it's a conversion to virtual base; I suppose we could use "member 
access within" until the library is updated.

> :(.  Well, for NULL one could argue that it was never a pointer to an object
> and never will be, but as it could be non-NULL pointer refering to an out of
> life object (e.g. deleted), I guess we have to stop instrumenting any
> "member accesses" if it is surrounded by ADDR_EXPR, right?

That's my interpretation.  It might be worth discussing with the clang 
folks what their rationale is.

> Based on the DR597 resolution, I guess the only cases -fsanitize=vptr
> can instrument are those listed in those bullets there:
> - the pointer is used to access a non-static data member or call a non-static member function of the object, or
> - the pointer is implicitly converted (4.10 [conv.ptr]) to a pointer to a virtual base class type, or
> - the pointer is used as the operand of a static_cast (5.2.9 [expr.static.cast]) except when the conversion is to pointer to cv void, or to pointer to cv void and subsequently to pointer to cv char or pointer to cv unsigned char, or
> - the pointer is used as the operand of a dynamic_cast (5.2.7 [expr.dynamic.cast])...
>
> the first bullet is supposedly instrumented in the patch (except we
> instrument ADDR_EXPR we shouldn't), the second, is that what vptr-5.C
> above is about?

Yes.

> the third one is partially the build_static_cast_1,
> but we only instrument downcasts, shouldn't we instrument upcasts too,
> or static_cast conversions to say POD types other than to
> void/char pointers?

I guess so.  This passage is specifically about references to storage 
either before or after the lifetime of an object, so it's not entirely 
clear that it applies here, but I think it does.

> And for the last one, should we before dynamic_cast verify the object
> passed to dynamic_cast has the expected vptr?

Perhaps we should just add checking to the dynamic_cast code.  I'm not 
sure if that would slow it down enough to merit a separate entry point 
for checking.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] -fsanitize=vptr instrumentation
  2014-09-17 18:02     ` Jason Merrill
@ 2014-09-17 20:42       ` Jason Merrill
  0 siblings, 0 replies; 16+ messages in thread
From: Jason Merrill @ 2014-09-17 20:42 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, gcc-patches

On 09/17/2014 02:02 PM, Jason Merrill wrote:
> On 09/17/2014 11:40 AM, Jakub Jelinek wrote:
>> And for the last one, should we before dynamic_cast verify the object
>> passed to dynamic_cast has the expected vptr?

> Perhaps we should just add checking to the dynamic_cast code.  I'm not
> sure if that would slow it down enough to merit a separate entry point
> for checking.

There is some checking I want to do in dynamic_cast, but I think we also 
want to verify that the argument has the type expected.

Jason


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-09-17 14:27 ` Jason Merrill
  2014-09-17 15:40   ` Jakub Jelinek
@ 2014-10-27 16:21   ` Jakub Jelinek
  2014-10-28 12:47     ` Jakub Jelinek
  1 sibling, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-10-27 16:21 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

On Wed, Sep 17, 2014 at 10:27:02AM -0400, Jason Merrill wrote:
> On 09/16/2014 10:56 AM, Jakub Jelinek wrote:
> >vptr-5.C is one Jason mailed me yesterday, clang++ doesn't instrument this
> >and g++ right now doesn't either, build_static_cast_1 certainly isn't called
> >in that case, and I must say I have no idea what should be checked there,
> >where etc.
> 
> What needs to be checked is conversion (in this case implicit) to a virtual
> base; if the vptr doesn't point to a vtable that has the appropriate vbase
> offset, we should complain.
> 
> virtual base conversions are implemented in build_base_path under if
> (virtual_access).

Implemented now.

> >vptr-6.C shows where the this optimization is performed and where it isn't
> >(clang++ has 10 instrumentations in T::h and 1 in S::l, g++ has fewer than
> >that, but not 0 in T::h (1 in S::l is right and needed I think)).
> 
> I agree that 0 is enough for T::h and 1 for S::l.

Based on the further discussions, I've dropped the this optimizations,
perhaps we can tweak the sanopt pass to optimize some unnecessary checks
later on.

> >I hope all of f[1-6] is invalid, I really don't see how we could instrument
> >member accesses otherwise (we'd need to limit to not taking address of it);
> >NULL pointer shouldn't point at a valid object.

Based on the discussions, I'm not instrumenting &p->a or &*&p->a for now,
am instrumenting just p->a or *&p->a.  Until the WG decides what to do with
DR597/DR1531.

> I don't see anything in the standard saying that these are undefined, only
> that trying to access the (non-)object pointed to is undefined.  It would be
> undefined if a conversion to virtual base were involved, i.e.
> 
> struct V: virtual R { };
> 
> // undefined if p doesn't point to a V because of the conversion to
> // virtual base R
> int* f7 (V* p) { return &p->r; }

Yeah, this is instrumented now (including complaining if p is NULL here).

> These conditions were loosened in C++11 by DRs 597 and 1531; before that it
> was reasonable to regard f[1-6] as undefined, and perhaps clang is using the
> earlier interpretation.
> 
> >+  TREE_SIDE_EFFECTS (cond) = 1;
> ...
> >+  TREE_SIDE_EFFECTS (hash) = 1;
> 
> Why do you need to set TREE_SIDE_EFFECTS on these?

Not needed, dropped now.

> >+  if (current_function_decl == NULL_TREE
> >+      || lookup_attribute ("no_sanitize_undefined",
> >+			   DECL_ATTRIBUTES (current_function_decl)))
> >+    return NULL_TREE;
> 
> When would this be called outside a function?  If for namespace-scope
> variable initializers, I'd think we do want instrumentation.

Changed.

> >+  /* T t; t.foo (); doesn't need instrumentation, if the type is known.  */
> >+  if (is_addr
> >+      && TREE_CODE (op) == ADDR_EXPR
> >+      && DECL_P (TREE_OPERAND (op, 0))
> >+      && same_type_p (type,
> >+		      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (op, 0)))))
> >+    return NULL_TREE;
> 
> You might want to use resolves_to_fixed_type_p in the optimizations.

For now I'd prefer not to do further optimizations, until it is clear that
those optimizations wouldn't remove any checks that could trigger on invalid
programs.

Here is an updated patch, ok if bootstrap/testing passes (so far just
checked with
make -j16 -k check RUNTESTFLAGS='--target_board=unix\{-m32,-m64\} asan.exp tsan.exp ubsan.exp'
)?

I haven't implemented the upcast checks Alex Samsonov implemented in clang recently,
those aren't -fsanitize=vptr but -fsanitize=alignment and/or
-fsanitize=object-size, so can be done in an incremental patch.

Furthermore, not 100% sure there if say given:
struct S { int a; };
struct T : S { int b[1024]; };
S *foo (T *p) { return (S *) p; }
it is really undefined behavior if p (if not NULL) doesn't point to object
that has at least sizeof (T) bytes in it.  Where does C++ say that e.g.:
S *bar () { S s; return foo ((T *) &s); }
is undefined behavior (assuming the alignment is right)?
struct U { S s; int b[1024]; };
S *foo2 (U *p) { return (S *) p; }
S *bar2 () { S s; return foo2 ((U *) &s); }
is not instrumented and not undefined behavior (again, assuming the
alignment is right)?  I have in mind the common technique where even in GCC
sources e.g. for tree_node union we sometimes allocate smaller sizes (just
to cover the size of some union field), instead of whole union, or
allocating space just for parts of a structure (e.g. if there is a trailing
foo[1] array, allocation size can be done for offsetof(type, foo[0]) etc.).

2014-10-27  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.
cp/
	* config-lang.in (gtfiles): Add cp/cp-ubsan.c.
	* cp-gimplify.c (cp_genericize_r): Call
	cp_ubsan_maybe_instrument_member_call for member calls and
	cp_ubsan_maybe_instrument_member_access for member accesses.
	(cp_ubsan_check_member_access_r): New function.
	(cp_genericize_tree): Call cp_walk_tree with
	cp_ubsan_check_member_access_r callback for -fsanitize=vptr.
	* cp-tree.h (cp_ubsan_maybe_instrument_member_call,
	cp_ubsan_maybe_instrument_member_access,
	cp_ubsan_maybe_instrument_downcast,
	cp_ubsan_maybe_instrument_cast_to_vbase,
	cp_ubsan_fixup_downcast_instrumentation): 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): Handle 4 argument
	BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS{,_ABORT}.
	* 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.
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.
libsanitizer/
	* ubsan/ubsan_handlers.cc (__ubsan::TypeCheckKinds): Cherry pick
	upstream r219642.

--- gcc/opts.c.jj	2014-10-25 20:46:06.443427738 +0200
+++ gcc/opts.c	2014-10-27 10:48:06.855097104 +0100
@@ -1575,6 +1575,7 @@ common_handle_option (struct gcc_options
 		sizeof "returns-nonnull-attribute" - 1 },
 	      { "object-size", SANITIZE_OBJECT_SIZE,
 		sizeof "object-size" - 1 },
+	      { "vptr", SANITIZE_VPTR, sizeof "vptr" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
--- gcc/ubsan.h.jj	2014-10-25 20:46:06.460427425 +0200
+++ gcc/ubsan.h	2014-10-27 12:09:47.738576202 +0100
@@ -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.  */
--- gcc/sanitizer.def.jj	2014-10-25 20:46:06.374429010 +0200
+++ gcc/sanitizer.def	2014-10-27 13:54:41.087270591 +0100
@@ -433,3 +433,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN
 		      "__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)
--- gcc/cp/cp-tree.h.jj	2014-10-25 20:46:05.433446353 +0200
+++ gcc/cp/cp-tree.h	2014-10-27 12:41:59.263503080 +0100
@@ -6333,6 +6333,13 @@ extern vec<tree> cx_error_context
 /* 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_maybe_instrument_member_access (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);
+extern void cp_ubsan_fixup_downcast_instrumentation (tree *);
+
 /* -- end of C++ */
 
 #endif /* ! GCC_CP_TREE_H */
--- gcc/cp/cp-gimplify.c.jj	2014-10-25 20:46:05.484445413 +0200
+++ gcc/cp/cp-gimplify.c	2014-10-27 10:48:06.858097045 +0100
@@ -1193,9 +1193,11 @@ cp_genericize_r (tree *stmt_p, int *walk
 	*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)
@@ -1210,7 +1212,26 @@ cp_genericize_r (tree *stmt_p, int *walk
 		= 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);
+	    }
+	  else if (flag_sanitize & SANITIZE_VPTR)
+	    {
+	      tree fndecl = get_callee_fndecl (stmt);
+	      if (fndecl
+		  && DECL_BUILT_IN (fndecl)
+		  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+		switch (DECL_FUNCTION_CODE (fndecl))
+		  {
+		  case BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS:
+		  case BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT:
+		    cp_ubsan_fixup_downcast_instrumentation (stmt_p);
+		    break;
+		  default:
+		    break;
+		  }
 	    }
 	}
     }
@@ -1220,6 +1241,59 @@ cp_genericize_r (tree *stmt_p, int *walk
   return NULL;
 }
 
+/* Attempt to instrument member accesses inside of the function.  */
+
+static tree
+cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
+{
+  tree stmt = *stmt_p, t;
+  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;
+	  cp_walk_tree (&t, cp_ubsan_check_member_access_r,
+			(void *) stmt, NULL);
+	}
+      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, NULL, NULL);
+	}
+      break;
+    case COMPONENT_REF:
+      if (data == NULL)
+	cp_ubsan_maybe_instrument_member_access (stmt);
+      /* FALLTHRU */
+    default:
+      if (data && handled_component_p (stmt))
+	{
+	  int i, len = TREE_OPERAND_LENGTH (stmt);
+	  *walk_subtrees = 0;
+	  if (!handled_component_p (TREE_OPERAND (stmt, 0)))
+	    data = NULL;
+	  for (i = 0; i < len; i++)
+	    {
+	      cp_walk_tree (&TREE_OPERAND (stmt, i),
+			    cp_ubsan_check_member_access_r, data, NULL);
+	      data = NULL;
+	    }
+	}
+      break;
+    }
+  return NULL_TREE;
+}
+
 /* Lower C++ front end trees to GENERIC in T_P.  */
 
 static void
@@ -1233,6 +1307,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_walk_tree (t_p, cp_ubsan_check_member_access_r, NULL, NULL);
 }
 
 /* If a function that should end with a return in non-void
--- gcc/cp/class.c.jj	2014-10-13 10:09:24.000000000 +0200
+++ gcc/cp/class.c	2014-10-27 12:55:55.238445804 +0100
@@ -430,10 +430,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;
 
--- gcc/cp/constexpr.c.jj	2014-10-25 20:46:05.473445616 +0200
+++ gcc/cp/constexpr.c	2014-10-27 10:48:06.860097006 +0100
@@ -1137,8 +1137,20 @@ cxx_eval_call_expression (const constexp
   if (DECL_CLONED_FUNCTION_P (fun))
     fun = DECL_CLONED_FUNCTION (fun);
   if (is_builtin_fn (fun))
-    return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
-					   addr, non_constant_p, overflow_p);
+    {
+      /* Ignore -fsanitize=vptr instrumentation.  */
+      if ((flag_sanitize & SANITIZE_VPTR)
+	  && DECL_BUILT_IN_CLASS (fun) == BUILT_IN_NORMAL
+	  && (DECL_FUNCTION_CODE (fun)
+	      == ((flag_sanitize_recover & SANITIZE_VPTR)
+		  ? BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS
+		  : BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT))
+	  && call_expr_nargs (t) == 4)
+	return void_node;
+
+      return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
+					     addr, non_constant_p, overflow_p);
+    }
   if (!DECL_DECLARED_CONSTEXPR_P (fun))
     {
       if (!allow_non_constant)
--- gcc/cp/Make-lang.in.jj	2014-10-25 20:46:05.467445726 +0200
+++ gcc/cp/Make-lang.in	2014-10-27 10:48:06.860097006 +0100
@@ -78,7 +78,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.
  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)
--- gcc/cp/cp-ubsan.c.jj	2014-10-27 10:48:06.861096987 +0100
+++ gcc/cp/cp-ubsan.c	2014-10-27 14:15:58.599404060 +0100
@@ -0,0 +1,291 @@
+/* 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 "tree.h"
+#include "alloc-pool.h"
+#include "cgraph.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"
+
+/* Cached __ubsan_vptr_type_cache decl.  */
+static GTY(()) tree ubsan_vptr_type_cache_decl;
+
+/* Emit if (__ubsan_vptr_type_cache[hash & 127] != hash) or
+   if (op && __ubsan_vptr_type_cache[hash & 127] != hash) test around
+   call.  */
+
+static tree
+cp_ubsan_cache_test (location_t loc, tree op, tree hash, tree call,
+		     enum ubsan_null_ckind ckind)
+{
+  tree t = fold_build2_loc (loc, BIT_AND_EXPR, pointer_sized_int_node, hash,
+			    build_int_cst (pointer_sized_int_node, 127));
+  t = build4_loc (loc, ARRAY_REF, pointer_sized_int_node,
+		     ubsan_vptr_type_cache_decl, t, NULL_TREE, NULL_TREE);
+  tree cond = fold_build2_loc (loc, NE_EXPR, boolean_type_node, t, hash);
+  if (ckind == UBSAN_DOWNCAST_POINTER)
+    cond = fold_build2_loc (loc, TRUTH_ANDIF_EXPR, boolean_type_node,
+			    fold_build2_loc (loc, NE_EXPR, boolean_type_node,
+					     op,
+					     build_zero_cst (TREE_TYPE (op))),
+			    cond);
+  return fold_build3_loc (loc, COND_EXPR, void_type_node, cond, call,
+			  void_node);
+}
+
+/* Helper function for
+   cp_ubsan_maybe_instrument_{member_{call,access},downcast}.  */
+
+static tree
+cp_ubsan_maybe_instrument_vptr (location_t loc, tree op, tree type,
+				bool is_addr, enum ubsan_null_ckind ckind)
+{
+  if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
+    return NULL_TREE;
+
+  if (current_function_decl
+      && lookup_attribute ("no_sanitize_undefined",
+			   DECL_ATTRIBUTES (current_function_decl)))
+    return NULL_TREE;
+
+  type = TYPE_MAIN_VARIANT (type);
+  if (!CLASS_TYPE_P (type) || !CLASSTYPE_VTABLES (type))
+    return NULL_TREE;
+
+  /* T t; t.foo (); doesn't need instrumentation, if the type is known.  */
+  if (is_addr
+      && TREE_CODE (op) == ADDR_EXPR
+      && DECL_P (TREE_OPERAND (op, 0))
+      && same_type_p (type,
+		      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (op, 0)))))
+    return NULL_TREE;
+
+  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);
+  vptr = build1_loc (loc, SAVE_EXPR, uint64_type_node, vptr);
+  /* Hash in 2 different hashes of mangled type name with the value of
+     vptr pointer.  */
+  tree cst = wide_int_to_tree (uint64_type_node,
+			       wi::uhwi (((uint64_t) 0x9ddfea08 << 32)
+					 | 0xeb382d69, 64));
+  tree t1 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, 
+			     str_hash, vptr);
+  t1 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t1, cst);
+  t1 = build1_loc (loc, SAVE_EXPR, uint64_type_node, t1);
+  tree t2 = fold_build2_loc (loc, LSHIFT_EXPR, uint64_type_node,
+			     t1, build_int_cst (integer_type_node, 47));
+  t2 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, t2, t1);
+  t2 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, vptr, t2);
+  t2 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t2, cst);
+  t2 = build1_loc (loc, SAVE_EXPR, uint64_type_node, t2);
+  tree t3 = fold_build2_loc (loc, LSHIFT_EXPR, uint64_type_node,
+			     t2, build_int_cst (integer_type_node, 47));
+  t3 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, t3, t2);
+  t3 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t3, cst);
+  tree hash = fold_convert_loc (loc, pointer_sized_int_node, t3);
+  hash = build1_loc (loc, SAVE_EXPR, pointer_sized_int_node, hash);
+  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_STATIC (array) = 1;
+      DECL_EXTERNAL (array) = 1;
+      layout_decl (array, 0);
+      ubsan_vptr_type_cache_decl = array;
+    }
+  tree ti_decl = get_tinfo_decl (type);
+  mark_used (ti_decl);
+  tree data
+    = ubsan_create_data ("__ubsan_vptr_data", 1, &loc,
+			 ubsan_type_descriptor (type), NULL_TREE,
+			 build_address (ti_decl),
+			 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;
+  tree call = builtin_decl_explicit (bcode), ret;
+  /* As UBSAN_DOWNCAST_* is called from build_static_cast_1, and
+     UBSAN_CAST_TO_VBASE during build_base_path, emit there
+     only the call and add the guard when genericizing the call.  */
+  switch (ckind)
+    {
+    default:
+      call = build_call_expr_loc (loc, call, 3, data, op,
+				  ubsan_encode_value (hash));
+      TREE_SIDE_EFFECTS (call) = 1;
+      ret = cp_ubsan_cache_test (loc, op, hash, call, ckind);
+      break;
+    case UBSAN_DOWNCAST_POINTER:
+    case UBSAN_DOWNCAST_REFERENCE:
+    case UBSAN_CAST_TO_VBASE:
+      call = build_call_expr_loc (loc, call, 4, data, op,
+				  ubsan_encode_value (hash),
+				  build_int_cst (integer_type_node, ckind));
+      TREE_SIDE_EFFECTS (call) = 1;
+      ret = call;
+      break;
+    }
+  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), ret, op);
+}
+
+/* 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;
+}
+
+/* Instrument a member access.  */
+
+void
+cp_ubsan_maybe_instrument_member_access (tree stmt)
+{
+  if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1)))
+    return;
+
+  tree base = TREE_OPERAND (stmt, 0);
+  if (TREE_CODE (base) == COMPONENT_REF
+      && DECL_ARTIFICIAL (TREE_OPERAND (base, 1)))
+    {
+      tree base2 = TREE_OPERAND (base, 0);
+      while (TREE_CODE (base2) == COMPONENT_REF
+	     || TREE_CODE (base2) == ARRAY_REF
+	     || TREE_CODE (base2) == ARRAY_RANGE_REF)
+	base2 = TREE_OPERAND (base2, 0);
+      if (TREE_CODE (base2) != INDIRECT_REF
+	  && TREE_CODE (base2) != MEM_REF)
+	return;
+    }
+  else if (TREE_CODE (base) != INDIRECT_REF
+	   && TREE_CODE (base) != MEM_REF)
+    return;
+
+  base = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), base,
+					 TREE_TYPE (base), false,
+					 UBSAN_MEMBER_ACCESS);
+  if (base)
+    TREE_OPERAND (stmt, 0)
+      = build_fold_indirect_ref_loc (EXPR_LOCATION (stmt), base);
+}
+
+/* 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);
+}
+
+/* Fix up downcast instrumentation.  */
+
+void
+cp_ubsan_fixup_downcast_instrumentation (tree *stmt_p)
+{
+  tree stmt = *stmt_p;
+  if (call_expr_nargs (stmt) != 4)
+    return;
+
+  location_t loc = EXPR_LOCATION (stmt);
+  tree data = CALL_EXPR_ARG (stmt, 0);
+  tree op = CALL_EXPR_ARG (stmt, 1);
+  tree hash = CALL_EXPR_ARG (stmt, 2);
+  enum ubsan_null_ckind ckind
+    = (enum ubsan_null_ckind) tree_to_shwi (CALL_EXPR_ARG (stmt, 3));
+  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;
+  tree call = builtin_decl_explicit (bcode);
+  call = build_call_expr_loc (loc, call, 3, data, op,
+			      ubsan_encode_value (hash));
+  TREE_SIDE_EFFECTS (call) = 1;
+  *stmt_p = cp_ubsan_cache_test (loc, op, hash, call, ckind);
+}
+
+#include "gt-cp-cp-ubsan.h"
--- gcc/cp/typeck.c.jj	2014-10-25 20:46:05.448446076 +0200
+++ gcc/cp/typeck.c	2014-10-27 10:48:06.863096948 +0100
@@ -2424,8 +2424,8 @@ build_class_member_access_expr (tree obj
 	  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
@@ -6503,11 +6503,21 @@ build_static_cast_1 (tree type, tree exp
       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++.
 
@@ -6635,7 +6645,16 @@ build_static_cast_1 (tree type, tree exp
 			  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))
--- gcc/cp/config-lang.in.jj	2014-10-25 20:46:05.485445394 +0200
+++ gcc/cp/config-lang.in	2014-10-27 10:48:06.863096948 +0100
@@ -29,4 +29,4 @@ compilers="cc1plus\$(exeext)"
 
 target_libs="target-libstdc++-v3"
 
-gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c \$(srcdir)/cp/constexpr.c"
+gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c \$(srcdir)/cp/constexpr.c \$(srcdir)/cp/cp-ubsan.c"
--- gcc/flag-types.h.jj	2014-10-25 20:46:05.494445228 +0200
+++ gcc/flag-types.h	2014-10-27 10:48:06.864096929 +0100
@@ -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
 };
 
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2014-10-27 10:48:06.864096929 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2014-10-27 10:48:06.864096929 +0100
@@ -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> ();
--- gcc/testsuite/g++.dg/ubsan/vptr-5.C.jj	2014-10-27 10:48:06.864096929 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-5.C	2014-10-27 10:48:06.864096929 +0100
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-8.C.jj	2014-10-27 13:05:18.638094867 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-8.C	2014-10-27 16:26:58.213905581 +0100
@@ -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)" }
--- gcc/testsuite/g++.dg/ubsan/vptr-3.C.jj	2014-10-27 10:48:06.865096910 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-3.C	2014-10-27 10:48:06.865096910 +0100
@@ -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);
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-7.C.jj	2014-10-27 10:48:06.865096910 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-7.C	2014-10-27 10:48:06.865096910 +0100
@@ -0,0 +1,25 @@
+// { dg-do compile }
+// { 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" } }
--- gcc/testsuite/g++.dg/ubsan/vptr-9.C.jj	2014-10-27 13:40:24.441196253 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-9.C	2014-10-27 16:26:13.384728254 +0100
@@ -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)" }
--- gcc/testsuite/g++.dg/ubsan/vptr-2.C.jj	2014-10-27 10:48:06.865096910 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-2.C	2014-10-27 10:48:06.865096910 +0100
@@ -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);
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-6.C.jj	2014-10-27 10:48:06.866096890 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-6.C	2014-10-27 10:48:06.866096890 +0100
@@ -0,0 +1,31 @@
+// { dg-do compile }
+// { 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" } }
--- gcc/testsuite/g++.dg/ubsan/vptr-1.C.jj	2014-10-27 10:48:06.866096890 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-1.C	2014-10-27 10:48:06.866096890 +0100
@@ -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);
+}
--- libsanitizer/ubsan/ubsan_handlers.cc.jj	2014-09-24 11:08:04.184026152 +0200
+++ libsanitizer/ubsan/ubsan_handlers.cc	2014-10-27 12:22:34.151570646 +0100
@@ -28,10 +28,10 @@ static bool ignoreReport(SourceLocation
 }
 
 namespace __ubsan {
-  const char *TypeCheckKinds[] = {
+const char *TypeCheckKinds[] = {
     "load of", "store to", "reference binding to", "member access within",
-    "member call on", "constructor call on", "downcast of", "downcast of"
-  };
+    "member call on", "constructor call on", "downcast of", "downcast of",
+    "upcast of", "cast to virtual base of"};
 }
 
 static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,


	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-10-27 16:21   ` [PATCH] -fsanitize=vptr instrumentation (take 2) Jakub Jelinek
@ 2014-10-28 12:47     ` Jakub Jelinek
  2014-11-12 14:09       ` Patch ping: " Jakub Jelinek
  2014-11-26 16:35       ` Jason Merrill
  0 siblings, 2 replies; 16+ messages in thread
From: Jakub Jelinek @ 2014-10-28 12:47 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

On Mon, Oct 27, 2014 at 05:16:05PM +0100, Jakub Jelinek wrote:
> Here is an updated patch, ok if bootstrap/testing passes (so far just
> checked with
> make -j16 -k check RUNTESTFLAGS='--target_board=unix\{-m32,-m64\} asan.exp tsan.exp ubsan.exp'
> )?

Updated patch that passed bootstrap/regtest on i686-linux.
Only minor changes: ensure during the cp_walk_tree we don't walk same trees
more than once (with SAVE_EXPR resulted in huge compile times), tiny test
cleanups, and making sure __ubsan_vptr_type_cache decl has normal visibility
and is not considered local in shared libraries.

2014-10-28  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.
cp/
	* config-lang.in (gtfiles): Add cp/cp-ubsan.c.
	* cp-gimplify.c (cp_genericize_r): Call
	cp_ubsan_maybe_instrument_member_call for member calls and
	cp_ubsan_maybe_instrument_member_access for member accesses.
	(struct cp_ubsan_check_member_access_data): New type.
	(cp_ubsan_check_member_access_r): New function.
	(cp_genericize_tree): Call cp_walk_tree with
	cp_ubsan_check_member_access_r callback for -fsanitize=vptr.
	* cp-tree.h (cp_ubsan_maybe_instrument_member_call,
	cp_ubsan_maybe_instrument_member_access,
	cp_ubsan_maybe_instrument_downcast,
	cp_ubsan_maybe_instrument_cast_to_vbase,
	cp_ubsan_fixup_downcast_instrumentation): 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): Handle 4 argument
	BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS{,_ABORT}.
	* 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.
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.
libsanitizer/
	* ubsan/ubsan_handlers.cc (__ubsan::TypeCheckKinds): Cherry pick
	upstream r219642.

--- gcc/opts.c.jj	2014-10-25 20:46:06.443427738 +0200
+++ gcc/opts.c	2014-10-27 10:48:06.855097104 +0100
@@ -1575,6 +1575,7 @@ common_handle_option (struct gcc_options
 		sizeof "returns-nonnull-attribute" - 1 },
 	      { "object-size", SANITIZE_OBJECT_SIZE,
 		sizeof "object-size" - 1 },
+	      { "vptr", SANITIZE_VPTR, sizeof "vptr" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
--- gcc/ubsan.h.jj	2014-10-25 20:46:06.460427425 +0200
+++ gcc/ubsan.h	2014-10-27 12:09:47.738576202 +0100
@@ -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.  */
--- gcc/sanitizer.def.jj	2014-10-25 20:46:06.374429010 +0200
+++ gcc/sanitizer.def	2014-10-27 13:54:41.087270591 +0100
@@ -433,3 +433,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN
 		      "__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)
--- gcc/cp/cp-tree.h.jj	2014-10-25 20:46:05.433446353 +0200
+++ gcc/cp/cp-tree.h	2014-10-27 12:41:59.263503080 +0100
@@ -6333,6 +6333,13 @@ extern vec<tree> cx_error_context
 /* 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_maybe_instrument_member_access (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);
+extern void cp_ubsan_fixup_downcast_instrumentation (tree *);
+
 /* -- end of C++ */
 
 #endif /* ! GCC_CP_TREE_H */
--- gcc/cp/cp-gimplify.c.jj	2014-10-25 20:46:05.484445413 +0200
+++ gcc/cp/cp-gimplify.c	2014-10-27 23:35:31.809141262 +0100
@@ -1193,9 +1193,11 @@ cp_genericize_r (tree *stmt_p, int *walk
 	*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)
@@ -1210,7 +1212,26 @@ cp_genericize_r (tree *stmt_p, int *walk
 		= 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);
+	    }
+	  else if (flag_sanitize & SANITIZE_VPTR)
+	    {
+	      tree fndecl = get_callee_fndecl (stmt);
+	      if (fndecl
+		  && DECL_BUILT_IN (fndecl)
+		  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+		switch (DECL_FUNCTION_CODE (fndecl))
+		  {
+		  case BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS:
+		  case BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT:
+		    cp_ubsan_fixup_downcast_instrumentation (stmt_p);
+		    break;
+		  default:
+		    break;
+		  }
 	    }
 	}
     }
@@ -1220,6 +1241,72 @@ cp_genericize_r (tree *stmt_p, int *walk
   return NULL;
 }
 
+/* Data passed to cp_ubsan_check_member_access_r.  */
+
+struct cp_ubsan_check_member_access_data
+{
+  hash_set<tree> *pset;
+  bool is_addr;
+};
+
+/* Attempt to instrument member accesses inside of the function.  */
+
+static tree
+cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
+{
+  tree stmt = *stmt_p, t;
+  struct cp_ubsan_check_member_access_data *ucmd
+    = (struct 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);
+      /* 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;
+}
+
 /* Lower C++ front end trees to GENERIC in T_P.  */
 
 static void
@@ -1233,6 +1320,14 @@ 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)
+    {
+      hash_set<tree> pset;
+      struct 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);
+    }
 }
 
 /* If a function that should end with a return in non-void
--- gcc/cp/class.c.jj	2014-10-13 10:09:24.000000000 +0200
+++ gcc/cp/class.c	2014-10-27 12:55:55.238445804 +0100
@@ -430,10 +430,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;
 
--- gcc/cp/constexpr.c.jj	2014-10-25 20:46:05.473445616 +0200
+++ gcc/cp/constexpr.c	2014-10-27 10:48:06.860097006 +0100
@@ -1137,8 +1137,20 @@ cxx_eval_call_expression (const constexp
   if (DECL_CLONED_FUNCTION_P (fun))
     fun = DECL_CLONED_FUNCTION (fun);
   if (is_builtin_fn (fun))
-    return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
-					   addr, non_constant_p, overflow_p);
+    {
+      /* Ignore -fsanitize=vptr instrumentation.  */
+      if ((flag_sanitize & SANITIZE_VPTR)
+	  && DECL_BUILT_IN_CLASS (fun) == BUILT_IN_NORMAL
+	  && (DECL_FUNCTION_CODE (fun)
+	      == ((flag_sanitize_recover & SANITIZE_VPTR)
+		  ? BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS
+		  : BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT))
+	  && call_expr_nargs (t) == 4)
+	return void_node;
+
+      return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
+					     addr, non_constant_p, overflow_p);
+    }
   if (!DECL_DECLARED_CONSTEXPR_P (fun))
     {
       if (!allow_non_constant)
--- gcc/cp/Make-lang.in.jj	2014-10-25 20:46:05.467445726 +0200
+++ gcc/cp/Make-lang.in	2014-10-27 10:48:06.860097006 +0100
@@ -78,7 +78,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.
  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)
--- gcc/cp/cp-ubsan.c.jj	2014-10-27 10:48:06.861096987 +0100
+++ gcc/cp/cp-ubsan.c	2014-10-27 14:15:58.599404060 +0100
@@ -0,0 +1,294 @@
+/* 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 "tree.h"
+#include "alloc-pool.h"
+#include "cgraph.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"
+
+/* Cached __ubsan_vptr_type_cache decl.  */
+static GTY(()) tree ubsan_vptr_type_cache_decl;
+
+/* Emit if (__ubsan_vptr_type_cache[hash & 127] != hash) or
+   if (op && __ubsan_vptr_type_cache[hash & 127] != hash) test around
+   call.  */
+
+static tree
+cp_ubsan_cache_test (location_t loc, tree op, tree hash, tree call,
+		     enum ubsan_null_ckind ckind)
+{
+  tree t = fold_build2_loc (loc, BIT_AND_EXPR, pointer_sized_int_node, hash,
+			    build_int_cst (pointer_sized_int_node, 127));
+  t = build4_loc (loc, ARRAY_REF, pointer_sized_int_node,
+		     ubsan_vptr_type_cache_decl, t, NULL_TREE, NULL_TREE);
+  tree cond = fold_build2_loc (loc, NE_EXPR, boolean_type_node, t, hash);
+  if (ckind == UBSAN_DOWNCAST_POINTER)
+    cond = fold_build2_loc (loc, TRUTH_ANDIF_EXPR, boolean_type_node,
+			    fold_build2_loc (loc, NE_EXPR, boolean_type_node,
+					     op,
+					     build_zero_cst (TREE_TYPE (op))),
+			    cond);
+  return fold_build3_loc (loc, COND_EXPR, void_type_node, cond, call,
+			  void_node);
+}
+
+/* Helper function for
+   cp_ubsan_maybe_instrument_{member_{call,access},downcast}.  */
+
+static tree
+cp_ubsan_maybe_instrument_vptr (location_t loc, tree op, tree type,
+				bool is_addr, enum ubsan_null_ckind ckind)
+{
+  if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
+    return NULL_TREE;
+
+  if (current_function_decl
+      && lookup_attribute ("no_sanitize_undefined",
+			   DECL_ATTRIBUTES (current_function_decl)))
+    return NULL_TREE;
+
+  type = TYPE_MAIN_VARIANT (type);
+  if (!CLASS_TYPE_P (type) || !CLASSTYPE_VTABLES (type))
+    return NULL_TREE;
+
+  /* T t; t.foo (); doesn't need instrumentation, if the type is known.  */
+  if (is_addr
+      && TREE_CODE (op) == ADDR_EXPR
+      && DECL_P (TREE_OPERAND (op, 0))
+      && same_type_p (type,
+		      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (op, 0)))))
+    return NULL_TREE;
+
+  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);
+  vptr = build1_loc (loc, SAVE_EXPR, uint64_type_node, vptr);
+  /* Hash in 2 different hashes of mangled type name with the value of
+     vptr pointer.  */
+  tree cst = wide_int_to_tree (uint64_type_node,
+			       wi::uhwi (((uint64_t) 0x9ddfea08 << 32)
+					 | 0xeb382d69, 64));
+  tree t1 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, 
+			     str_hash, vptr);
+  t1 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t1, cst);
+  t1 = build1_loc (loc, SAVE_EXPR, uint64_type_node, t1);
+  tree t2 = fold_build2_loc (loc, LSHIFT_EXPR, uint64_type_node,
+			     t1, build_int_cst (integer_type_node, 47));
+  t2 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, t2, t1);
+  t2 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, vptr, t2);
+  t2 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t2, cst);
+  t2 = build1_loc (loc, SAVE_EXPR, uint64_type_node, t2);
+  tree t3 = fold_build2_loc (loc, LSHIFT_EXPR, uint64_type_node,
+			     t2, build_int_cst (integer_type_node, 47));
+  t3 = fold_build2_loc (loc, BIT_XOR_EXPR, uint64_type_node, t3, t2);
+  t3 = fold_build2_loc (loc, MULT_EXPR, uint64_type_node, t3, cst);
+  tree hash = fold_convert_loc (loc, pointer_sized_int_node, t3);
+  hash = build1_loc (loc, SAVE_EXPR, pointer_sized_int_node, hash);
+  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;
+      layout_decl (array, 0);
+      ubsan_vptr_type_cache_decl = array;
+    }
+  tree ti_decl = get_tinfo_decl (type);
+  mark_used (ti_decl);
+  tree data
+    = ubsan_create_data ("__ubsan_vptr_data", 1, &loc,
+			 ubsan_type_descriptor (type), NULL_TREE,
+			 build_address (ti_decl),
+			 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;
+  tree call = builtin_decl_explicit (bcode), ret;
+  /* As UBSAN_DOWNCAST_* is called from build_static_cast_1, and
+     UBSAN_CAST_TO_VBASE during build_base_path, emit there
+     only the call and add the guard when genericizing the call.  */
+  switch (ckind)
+    {
+    default:
+      call = build_call_expr_loc (loc, call, 3, data, op,
+				  ubsan_encode_value (hash));
+      TREE_SIDE_EFFECTS (call) = 1;
+      ret = cp_ubsan_cache_test (loc, op, hash, call, ckind);
+      break;
+    case UBSAN_DOWNCAST_POINTER:
+    case UBSAN_DOWNCAST_REFERENCE:
+    case UBSAN_CAST_TO_VBASE:
+      call = build_call_expr_loc (loc, call, 4, data, op,
+				  ubsan_encode_value (hash),
+				  build_int_cst (integer_type_node, ckind));
+      TREE_SIDE_EFFECTS (call) = 1;
+      ret = call;
+      break;
+    }
+  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), ret, op);
+}
+
+/* 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;
+}
+
+/* Instrument a member access.  */
+
+void
+cp_ubsan_maybe_instrument_member_access (tree stmt)
+{
+  if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1)))
+    return;
+
+  tree base = TREE_OPERAND (stmt, 0);
+  if (TREE_CODE (base) == COMPONENT_REF
+      && DECL_ARTIFICIAL (TREE_OPERAND (base, 1)))
+    {
+      tree base2 = TREE_OPERAND (base, 0);
+      while (TREE_CODE (base2) == COMPONENT_REF
+	     || TREE_CODE (base2) == ARRAY_REF
+	     || TREE_CODE (base2) == ARRAY_RANGE_REF)
+	base2 = TREE_OPERAND (base2, 0);
+      if (TREE_CODE (base2) != INDIRECT_REF
+	  && TREE_CODE (base2) != MEM_REF)
+	return;
+    }
+  else if (TREE_CODE (base) != INDIRECT_REF
+	   && TREE_CODE (base) != MEM_REF)
+    return;
+
+  base = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), base,
+					 TREE_TYPE (base), false,
+					 UBSAN_MEMBER_ACCESS);
+  if (base)
+    TREE_OPERAND (stmt, 0)
+      = build_fold_indirect_ref_loc (EXPR_LOCATION (stmt), base);
+}
+
+/* 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);
+}
+
+/* Fix up downcast instrumentation.  */
+
+void
+cp_ubsan_fixup_downcast_instrumentation (tree *stmt_p)
+{
+  tree stmt = *stmt_p;
+  if (call_expr_nargs (stmt) != 4)
+    return;
+
+  location_t loc = EXPR_LOCATION (stmt);
+  tree data = CALL_EXPR_ARG (stmt, 0);
+  tree op = CALL_EXPR_ARG (stmt, 1);
+  tree hash = CALL_EXPR_ARG (stmt, 2);
+  enum ubsan_null_ckind ckind
+    = (enum ubsan_null_ckind) tree_to_shwi (CALL_EXPR_ARG (stmt, 3));
+  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;
+  tree call = builtin_decl_explicit (bcode);
+  call = build_call_expr_loc (loc, call, 3, data, op,
+			      ubsan_encode_value (hash));
+  TREE_SIDE_EFFECTS (call) = 1;
+  *stmt_p = cp_ubsan_cache_test (loc, op, hash, call, ckind);
+}
+
+#include "gt-cp-cp-ubsan.h"
--- gcc/cp/typeck.c.jj	2014-10-25 20:46:05.448446076 +0200
+++ gcc/cp/typeck.c	2014-10-27 10:48:06.863096948 +0100
@@ -2424,8 +2424,8 @@ build_class_member_access_expr (tree obj
 	  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
@@ -6503,11 +6503,21 @@ build_static_cast_1 (tree type, tree exp
       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++.
 
@@ -6635,7 +6645,16 @@ build_static_cast_1 (tree type, tree exp
 			  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))
--- gcc/cp/config-lang.in.jj	2014-10-25 20:46:05.485445394 +0200
+++ gcc/cp/config-lang.in	2014-10-27 10:48:06.863096948 +0100
@@ -29,4 +29,4 @@ compilers="cc1plus\$(exeext)"
 
 target_libs="target-libstdc++-v3"
 
-gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c \$(srcdir)/cp/constexpr.c"
+gtfiles="\$(srcdir)/cp/rtti.c \$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.h \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-common.h \$(srcdir)/c-family/c-objc.h \$(srcdir)/c-family/c-lex.c \$(srcdir)/c-family/c-pragma.h \$(srcdir)/c-family/c-pragma.c \$(srcdir)/cp/class.c \$(srcdir)/cp/cp-objcp-common.c \$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/except.c \$(srcdir)/cp/vtable-class-hierarchy.c \$(srcdir)/cp/constexpr.c \$(srcdir)/cp/cp-ubsan.c"
--- gcc/flag-types.h.jj	2014-10-25 20:46:05.494445228 +0200
+++ gcc/flag-types.h	2014-10-27 10:48:06.864096929 +0100
@@ -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
 };
 
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2014-10-27 10:48:06.864096929 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2014-10-27 10:48:06.864096929 +0100
@@ -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> ();
--- gcc/testsuite/g++.dg/ubsan/vptr-5.C.jj	2014-10-27 10:48:06.864096929 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-5.C	2014-10-27 10:48:06.864096929 +0100
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-8.C.jj	2014-10-27 13:05:18.638094867 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-8.C	2014-10-27 16:26:58.213905581 +0100
@@ -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)" }
--- gcc/testsuite/g++.dg/ubsan/vptr-3.C.jj	2014-10-27 10:48:06.865096910 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-3.C	2014-10-27 10:48:06.865096910 +0100
@@ -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);
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-7.C.jj	2014-10-27 10:48:06.865096910 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-7.C	2014-10-27 21:40:40.950984610 +0100
@@ -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" } }
--- gcc/testsuite/g++.dg/ubsan/vptr-9.C.jj	2014-10-27 13:40:24.441196253 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-9.C	2014-10-27 16:26:13.384728254 +0100
@@ -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)" }
--- gcc/testsuite/g++.dg/ubsan/vptr-2.C.jj	2014-10-27 10:48:06.865096910 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-2.C	2014-10-27 10:48:06.865096910 +0100
@@ -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);
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-6.C.jj	2014-10-27 10:48:06.866096890 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-6.C	2014-10-27 21:40:31.619148815 +0100
@@ -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" } }
--- gcc/testsuite/g++.dg/ubsan/vptr-1.C.jj	2014-10-27 10:48:06.866096890 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-1.C	2014-10-27 10:48:06.866096890 +0100
@@ -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);
+}
--- libsanitizer/ubsan/ubsan_handlers.cc.jj	2014-09-24 11:08:04.184026152 +0200
+++ libsanitizer/ubsan/ubsan_handlers.cc	2014-10-27 12:22:34.151570646 +0100
@@ -28,10 +28,10 @@ static bool ignoreReport(SourceLocation
 }
 
 namespace __ubsan {
-  const char *TypeCheckKinds[] = {
+const char *TypeCheckKinds[] = {
     "load of", "store to", "reference binding to", "member access within",
-    "member call on", "constructor call on", "downcast of", "downcast of"
-  };
+    "member call on", "constructor call on", "downcast of", "downcast of",
+    "upcast of", "cast to virtual base of"};
 }
 
 static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,


	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Patch ping: [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-10-28 12:47     ` Jakub Jelinek
@ 2014-11-12 14:09       ` Jakub Jelinek
  2014-11-26  9:01         ` Patch ping^2: " Jakub Jelinek
  2014-11-26 16:35       ` Jason Merrill
  1 sibling, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-11-12 14:09 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

Hi!

On Tue, Oct 28, 2014 at 01:44:50PM +0100, Jakub Jelinek wrote:
> On Mon, Oct 27, 2014 at 05:16:05PM +0100, Jakub Jelinek wrote:
> > Here is an updated patch, ok if bootstrap/testing passes (so far just
> > checked with
> > make -j16 -k check RUNTESTFLAGS='--target_board=unix\{-m32,-m64\} asan.exp tsan.exp ubsan.exp'
> > )?

I'd like to ping the
https://gcc.gnu.org/ml/gcc-patches/2014-10/msg02945.html
patch.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Patch ping^2: [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-11-12 14:09       ` Patch ping: " Jakub Jelinek
@ 2014-11-26  9:01         ` Jakub Jelinek
  0 siblings, 0 replies; 16+ messages in thread
From: Jakub Jelinek @ 2014-11-26  9:01 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Marek Polacek, gcc-patches

On Wed, Nov 12, 2014 at 03:05:46PM +0100, Jakub Jelinek wrote:
> On Tue, Oct 28, 2014 at 01:44:50PM +0100, Jakub Jelinek wrote:
> > On Mon, Oct 27, 2014 at 05:16:05PM +0100, Jakub Jelinek wrote:
> > > Here is an updated patch, ok if bootstrap/testing passes (so far just
> > > checked with
> > > make -j16 -k check RUNTESTFLAGS='--target_board=unix\{-m32,-m64\} asan.exp tsan.exp ubsan.exp'
> > > )?
> 
> I'd like to ping the
> https://gcc.gnu.org/ml/gcc-patches/2014-10/msg02945.html
> patch.

Ping.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-10-28 12:47     ` Jakub Jelinek
  2014-11-12 14:09       ` Patch ping: " Jakub Jelinek
@ 2014-11-26 16:35       ` Jason Merrill
  2014-11-28 15:13         ` Jakub Jelinek
  1 sibling, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2014-11-26 16:35 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

On 10/28/2014 08:44 AM, Jakub Jelinek wrote:
> +cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)

This function needs a longer comment about exactly what forms it's 
trying to instrument.

> +  /* T t; t.foo (); doesn't need instrumentation, if the type is known.  */
> +  if (is_addr
> +      && TREE_CODE (op) == ADDR_EXPR
> +      && DECL_P (TREE_OPERAND (op, 0))
> +      && same_type_p (type,
> +		      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (op, 0)))))
> +    return NULL_TREE;

How do we know the decl's vptr hasn't been clobbered?  This seems like 
one of the optimizations we decided to drop.

> +  if (TREE_CODE (base) == COMPONENT_REF
> +      && DECL_ARTIFICIAL (TREE_OPERAND (base, 1)))
> +    {
> +      tree base2 = TREE_OPERAND (base, 0);
> +      while (TREE_CODE (base2) == COMPONENT_REF
> +	     || TREE_CODE (base2) == ARRAY_REF
> +	     || TREE_CODE (base2) == ARRAY_RANGE_REF)
> +	base2 = TREE_OPERAND (base2, 0);
> +      if (TREE_CODE (base2) != INDIRECT_REF
> +	  && TREE_CODE (base2) != MEM_REF)
> +	return;
> +    }
> +  else if (TREE_CODE (base) != INDIRECT_REF
> +	   && TREE_CODE (base) != MEM_REF)
> +    return;

Why do you look through ARRAY_REF here?  An element of an array is its 
own complete object.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-11-26 16:35       ` Jason Merrill
@ 2014-11-28 15:13         ` Jakub Jelinek
  2014-12-01 14:45           ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-11-28 15:13 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

On Wed, Nov 26, 2014 at 11:18:11AM -0500, Jason Merrill wrote:
> On 10/28/2014 08:44 AM, Jakub Jelinek wrote:
> >+cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
> 
> This function needs a longer comment about exactly what forms it's trying to
> instrument.

Ok, will do.

> >+  /* T t; t.foo (); doesn't need instrumentation, if the type is known.  */
> >+  if (is_addr
> >+      && TREE_CODE (op) == ADDR_EXPR
> >+      && DECL_P (TREE_OPERAND (op, 0))
> >+      && same_type_p (type,
> >+		      TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (op, 0)))))
> >+    return NULL_TREE;
> 
> How do we know the decl's vptr hasn't been clobbered?  This seems like one
> of the optimizations we decided to drop.

Yeah, will try to remove this hunk and see what it changes.

> >+  if (TREE_CODE (base) == COMPONENT_REF
> >+      && DECL_ARTIFICIAL (TREE_OPERAND (base, 1)))
> >+    {
> >+      tree base2 = TREE_OPERAND (base, 0);
> >+      while (TREE_CODE (base2) == COMPONENT_REF
> >+	     || TREE_CODE (base2) == ARRAY_REF
> >+	     || TREE_CODE (base2) == ARRAY_RANGE_REF)
> >+	base2 = TREE_OPERAND (base2, 0);
> >+      if (TREE_CODE (base2) != INDIRECT_REF
> >+	  && TREE_CODE (base2) != MEM_REF)
> >+	return;
> >+    }
> >+  else if (TREE_CODE (base) != INDIRECT_REF
> >+	   && TREE_CODE (base) != MEM_REF)
> >+    return;
> 
> Why do you look through ARRAY_REF here?  An element of an array is its own
> complete object.

That had to do with only instrumenting dereferences surrounded by handled
components, but not accesses to decls (so p->x gets instrumented but
q.x for VAR_DECL q is not).  If we want to instrument that, then I'll need
to tweak the member access instrumentation some more.

Today I found that the C++14 constexpr changes unfortunately mean I have to
make bigger changes, so I'm rewriting it to use UBSAN_VPTR internal calls
and only lower that during sanopt (and will try to optimize clearly
unnecessary checks at that point to offset from the FE instrumenting more
- if the optimizers can prove virtual table of the object has not been
changed since dominating UBSAN_VPTR, we don't need to instrument it again.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-11-28 15:13         ` Jakub Jelinek
@ 2014-12-01 14:45           ` Jason Merrill
  2014-12-03 12:01             ` Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2014-12-01 14:45 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

On 11/28/2014 09:41 AM, Jakub Jelinek wrote:
>> Why do you look through ARRAY_REF here?  An element of an array is its own
>> complete object.
>
> That had to do with only instrumenting dereferences surrounded by handled
> components, but not accesses to decls (so p->x gets instrumented but
> q.x for VAR_DECL q is not).

That also seems like an optimization we decided we don't want; we know 
what type q was declared as, but its vptr might have gotten clobbered by 
code with undefined behavior.

Jason


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-12-01 14:45           ` Jason Merrill
@ 2014-12-03 12:01             ` Jakub Jelinek
  2014-12-03 13:38               ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-12-03 12:01 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

On Mon, Dec 01, 2014 at 09:45:30AM -0500, Jason Merrill wrote:
> On 11/28/2014 09:41 AM, Jakub Jelinek wrote:
> >>Why do you look through ARRAY_REF here?  An element of an array is its own
> >>complete object.
> >
> >That had to do with only instrumenting dereferences surrounded by handled
> >components, but not accesses to decls (so p->x gets instrumented but
> >q.x for VAR_DECL q is not).
> 
> That also seems like an optimization we decided we don't want; we know what
> type q was declared as, but its vptr might have gotten clobbered by code
> with undefined behavior.

One more question.  My current version of the patch adds one ubsan vptr
instrumentation in each of the following functions:

struct S { int s; virtual void foo (); S(); virtual ~S(); };
struct T : S {};
struct U { int u; S s[4]; };
struct V { U v; virtual void bar (); V(); virtual ~V(); };
V v;

int
f1 (V *p)
{
  return p->v.u;
}

int
f2 (V *p)
{
  return p->v.s[2].s;
}

int
f3 ()
{
  return v.v.u;
}

int
f4 ()
{
  return v.v.s[2].s;
}

(in f1 and f3 verifies it for _ZTI1V, in f2 and f4 verifies it for
_ZTI1S).  Should I change it so that we get 2 instrumentations in f2 and f4
and one in f1/f3 (i.e. in f2/f4 check two vptrs, one _ZTI1S and one _ZTI1V),
or do we care just about the outermost one?
Note, latest clang has 1 instrumentation in f1 and f4, two in f2 and none
in f3.  I think we've agreed we want to instrument even normal decl member
accesses and method calls.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] -fsanitize=vptr instrumentation (take 2)
  2014-12-03 12:01             ` Jakub Jelinek
@ 2014-12-03 13:38               ` Jason Merrill
  2014-12-03 17:03                 ` [PATCH] -fsanitize=vptr instrumentation (take 3) Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2014-12-03 13:38 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

On 12/03/2014 07:01 AM, Jakub Jelinek wrote:
> One more question.  My current version of the patch adds one ubsan vptr
> instrumentation in each of the following functions:
>
> struct S { int s; virtual void foo (); S(); virtual ~S(); };
> struct T : S {};
> struct U { int u; S s[4]; };
> struct V { U v; virtual void bar (); V(); virtual ~V(); };
> V v;
>
> int
> f1 (V *p)
> {
>    return p->v.u;
> }
>
> int
> f2 (V *p)
> {
>    return p->v.s[2].s;
> }
>
> int
> f3 ()
> {
>    return v.v.u;
> }
>
> int
> f4 ()
> {
>    return v.v.s[2].s;
> }
>
> (in f1 and f3 verifies it for _ZTI1V, in f2 and f4 verifies it for
> _ZTI1S).  Should I change it so that we get 2 instrumentations in f2 and f4
> and one in f1/f3 (i.e. in f2/f4 check two vptrs, one _ZTI1S and one _ZTI1V),
> or do we care just about the outermost one?

I think so; it definitely is possible to clobber the vptr in a normal 
decl and the check would catch that.  Though perhaps we want a separate 
mode that optimizes away the checks when we know the declared type of 
the object.

> Note, latest clang has 1 instrumentation in f1 and f4, two in f2 and none
> in f3.

That sounds like they mean to avoid checking when there's a declared 
type but array-to-pointer decay is causing them to check the array members.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH] -fsanitize=vptr instrumentation (take 3)
  2014-12-03 13:38               ` Jason Merrill
@ 2014-12-03 17:03                 ` Jakub Jelinek
  2015-01-15 17:23                   ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-12-03 17:03 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

On Wed, Dec 03, 2014 at 08:38:44AM -0500, Jason Merrill wrote:
> >(in f1 and f3 verifies it for _ZTI1V, in f2 and f4 verifies it for
> >_ZTI1S).  Should I change it so that we get 2 instrumentations in f2 and f4
> >and one in f1/f3 (i.e. in f2/f4 check two vptrs, one _ZTI1S and one _ZTI1V),
> >or do we care just about the outermost one?
> 
> I think so; it definitely is possible to clobber the vptr in a normal decl
> and the check would catch that.

Ok, this updated patch implements it.
I had to partly rewrite it because the C++14 constexpr changes mean that
the conditionals, __ubsan_vptr_type_cache reads etc. would all appear in
constexpr functions.  So this patch instead uses an internal function,
UBSAN_VPTR, and only lowers that during sanopt late in the optimization.
The patch also instruments more member accesses and more member calls etc.
than the previous one did, on the other side I've added an optimization
(for -O+) that if the compiler can prove the virtual table has not changed
since previous UBSAN_VPTR checking the same address and vptr, the second
check is omitted.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2014-12-03  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.
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.

--- gcc/opts.c.jj	2014-12-03 11:52:41.331306754 +0100
+++ gcc/opts.c	2014-12-03 11:53:17.539682551 +0100
@@ -1599,6 +1599,7 @@ common_handle_option (struct gcc_options
 		sizeof "returns-nonnull-attribute" - 1 },
 	      { "object-size", SANITIZE_OBJECT_SIZE,
 		sizeof "object-size" - 1 },
+	      { "vptr", SANITIZE_VPTR, sizeof "vptr" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
--- gcc/ubsan.h.jj	2014-12-03 09:08:54.948354471 +0100
+++ gcc/ubsan.h	2014-12-03 11:53:17.539682551 +0100
@@ -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.  */
@@ -41,6 +45,7 @@ enum ubsan_print_style {
 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);
--- gcc/sanitizer.def.jj	2014-12-03 09:08:53.880373437 +0100
+++ gcc/sanitizer.def	2014-12-03 11:53:17.539682551 +0100
@@ -495,3 +495,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN
 		      "__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)
--- gcc/cp/cp-tree.h.jj	2014-12-03 09:08:54.229367239 +0100
+++ gcc/cp/cp-tree.h	2014-12-03 14:17:40.219649896 +0100
@@ -6392,6 +6392,12 @@ extern vec<tree> cx_error_context
 /* 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 */
--- gcc/cp/cp-gimplify.c.jj	2014-12-03 09:08:54.381364540 +0100
+++ gcc/cp/cp-gimplify.c	2014-12-03 13:42:02.882344918 +0100
@@ -1192,9 +1192,11 @@ cp_genericize_r (tree *stmt_p, int *walk
 	*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)
@@ -1209,7 +1211,10 @@ cp_genericize_r (tree *stmt_p, int *walk
 		= 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);
 	    }
 	}
     }
@@ -1232,6 +1237,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
--- gcc/cp/class.c.jj	2014-12-03 09:08:54.143368766 +0100
+++ gcc/cp/class.c	2014-12-03 11:53:17.545682448 +0100
@@ -441,10 +441,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;
 
--- gcc/cp/constexpr.c.jj	2014-12-03 09:09:02.068228029 +0100
+++ gcc/cp/constexpr.c	2014-12-03 11:53:17.546682431 +0100
@@ -1158,6 +1158,7 @@ cxx_eval_call_expression (const constexp
       {
       case IFN_UBSAN_NULL:
       case IFN_UBSAN_BOUNDS:
+      case IFN_UBSAN_VPTR:
 	return void_node;
       default:
 	if (!ctx->quiet)
@@ -3781,6 +3782,19 @@ potential_constant_expression_1 (tree t,
 
 	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),
--- gcc/cp/Make-lang.in.jj	2014-12-03 09:08:54.442363457 +0100
+++ gcc/cp/Make-lang.in	2014-12-03 11:53:17.546682431 +0100
@@ -78,7 +78,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.
  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)
--- gcc/cp/cp-ubsan.c.jj	2014-12-03 11:53:17.547682413 +0100
+++ gcc/cp/cp-ubsan.c	2014-12-03 14:18:36.266627416 +0100
@@ -0,0 +1,292 @@
+/* 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 "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);
+}
--- gcc/cp/typeck.c.jj	2014-12-03 09:08:54.266366582 +0100
+++ gcc/cp/typeck.c	2014-12-03 11:53:17.549682379 +0100
@@ -2425,8 +2425,8 @@ build_class_member_access_expr (tree obj
 	  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
@@ -6487,11 +6487,21 @@ build_static_cast_1 (tree type, tree exp
       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++.
 
@@ -6619,7 +6629,16 @@ build_static_cast_1 (tree type, tree exp
 			  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))
--- gcc/flag-types.h.jj	2014-12-03 09:08:54.829356584 +0100
+++ gcc/flag-types.h	2014-12-03 11:53:17.550682362 +0100
@@ -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
 };
 
--- gcc/cp/call.c.jj	2014-12-03 09:08:54.313365747 +0100
+++ gcc/cp/call.c	2014-12-03 11:53:17.554682293 +0100
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.
 #include "ipa-ref.h"
 #include "cgraph.h"
 #include "wide-int.h"
+#include "internal-fn.h"
 
 /* The various kinds of conversion.  */
 
@@ -333,13 +334,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;
--- gcc/internal-fn.c.jj	2014-12-03 09:08:53.819374520 +0100
+++ gcc/internal-fn.c	2014-12-03 11:53:17.556682258 +0100
@@ -147,7 +147,7 @@ expand_STORE_LANES (gcall *stmt)
 }
 
 static void
-expand_ANNOTATE (gcall *stmt ATTRIBUTE_UNUSED)
+expand_ANNOTATE (gcall *)
 {
   gcc_unreachable ();
 }
@@ -155,7 +155,7 @@ expand_ANNOTATE (gcall *stmt ATTRIBUTE_U
 /* 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 ();
 }
@@ -163,7 +163,7 @@ expand_GOMP_SIMD_LANE (gcall *stmt ATTRI
 /* 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 ();
 }
@@ -171,7 +171,7 @@ expand_GOMP_SIMD_VF (gcall *stmt ATTRIBU
 /* 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 ();
 }
@@ -179,7 +179,7 @@ expand_GOMP_SIMD_LAST_LANE (gcall *stmt
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_NULL (gcall *stmt ATTRIBUTE_UNUSED)
+expand_UBSAN_NULL (gcall *)
 {
   gcc_unreachable ();
 }
@@ -187,7 +187,7 @@ expand_UBSAN_NULL (gcall *stmt ATTRIBUTE
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_BOUNDS (gcall *stmt ATTRIBUTE_UNUSED)
+expand_UBSAN_BOUNDS (gcall *)
 {
   gcc_unreachable ();
 }
@@ -195,7 +195,7 @@ expand_UBSAN_BOUNDS (gcall *stmt ATTRIBU
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_OBJECT_SIZE (gcall *stmt ATTRIBUTE_UNUSED)
+expand_UBSAN_VPTR (gcall *)
 {
   gcc_unreachable ();
 }
@@ -203,7 +203,15 @@ expand_UBSAN_OBJECT_SIZE (gcall *stmt AT
 /* 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 ();
 }
@@ -1862,7 +1870,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 ();
 }
--- gcc/internal-fn.def.jj	2014-12-03 09:08:54.075369974 +0100
+++ gcc/internal-fn.def	2014-12-03 11:53:17.556682258 +0100
@@ -48,15 +48,16 @@ DEF_INTERNAL_FN (LOOP_VECTORIZED, ECF_NO
 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)
--- gcc/sanopt.c.jj	2014-12-03 11:52:41.331306754 +0100
+++ gcc/sanopt.c	2014-12-03 11:57:29.304342332 +0100
@@ -100,7 +100,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)
   {
@@ -113,6 +113,63 @@ struct tree_map_traits : default_hashmap
   }
 }; 
 
+/* 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.  */
 
@@ -124,7 +181,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;
@@ -298,6 +361,32 @@ maybe_optimize_ubsan_null_ifn (struct sa
   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.  */
 
@@ -488,6 +577,9 @@ sanopt_optimize_walker (basic_block bb,
 	  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);
@@ -592,7 +684,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)
     {
@@ -638,6 +731,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;
--- gcc/tree-ssa-alias.c.jj	2014-12-03 09:08:54.974354009 +0100
+++ gcc/tree-ssa-alias.c	2014-12-03 11:53:17.557682241 +0100
@@ -1911,6 +1911,22 @@ call_may_clobber_ref_p_1 (gcall *call, a
   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)
--- gcc/ubsan.c.jj	2014-12-03 09:08:54.884355607 +0100
+++ gcc/ubsan.c	2014-12-03 11:53:17.558682224 +0100
@@ -962,6 +962,167 @@ ubsan_expand_objsize_ifn (gimple_stmt_it
   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.  */
 
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2014-12-03 11:53:17.550682362 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2014-12-03 11:53:17.550682362 +0100
@@ -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> ();
--- gcc/testsuite/g++.dg/ubsan/vptr-5.C.jj	2014-12-03 11:53:17.550682362 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-5.C	2014-12-03 11:53:17.550682362 +0100
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-8.C.jj	2014-12-03 11:53:17.550682362 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-8.C	2014-12-03 11:53:17.550682362 +0100
@@ -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)" }
--- gcc/testsuite/g++.dg/ubsan/vptr-3.C.jj	2014-12-03 11:53:17.551682344 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-3.C	2014-12-03 11:53:17.551682344 +0100
@@ -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);
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-7.C.jj	2014-12-03 11:53:17.551682344 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-7.C	2014-12-03 11:53:17.551682344 +0100
@@ -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" } }
--- gcc/testsuite/g++.dg/ubsan/vptr-9.C.jj	2014-12-03 11:53:17.551682344 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-9.C	2014-12-03 11:53:17.551682344 +0100
@@ -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)" }
--- gcc/testsuite/g++.dg/ubsan/vptr-2.C.jj	2014-12-03 11:53:17.551682344 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-2.C	2014-12-03 11:53:17.551682344 +0100
@@ -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);
+}
--- gcc/testsuite/g++.dg/ubsan/vptr-6.C.jj	2014-12-03 11:53:17.552682327 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-6.C	2014-12-03 11:53:17.552682327 +0100
@@ -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" } }
--- gcc/testsuite/g++.dg/ubsan/vptr-1.C.jj	2014-12-03 11:53:17.552682327 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-1.C	2014-12-03 11:53:17.552682327 +0100
@@ -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);
+}


	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] -fsanitize=vptr instrumentation (take 3)
  2014-12-03 17:03                 ` [PATCH] -fsanitize=vptr instrumentation (take 3) Jakub Jelinek
@ 2015-01-15 17:23                   ` Jason Merrill
  0 siblings, 0 replies; 16+ messages in thread
From: Jason Merrill @ 2015-01-15 17:23 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Alexey Samsonov, Marek Polacek, gcc-patches

OK, sorry for the delay.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2015-01-15 17:11 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-16 14:56 [RFC PATCH] -fsanitize=vptr instrumentation Jakub Jelinek
2014-09-17 14:27 ` Jason Merrill
2014-09-17 15:40   ` Jakub Jelinek
2014-09-17 18:02     ` Jason Merrill
2014-09-17 20:42       ` Jason Merrill
2014-10-27 16:21   ` [PATCH] -fsanitize=vptr instrumentation (take 2) Jakub Jelinek
2014-10-28 12:47     ` Jakub Jelinek
2014-11-12 14:09       ` Patch ping: " Jakub Jelinek
2014-11-26  9:01         ` Patch ping^2: " Jakub Jelinek
2014-11-26 16:35       ` Jason Merrill
2014-11-28 15:13         ` Jakub Jelinek
2014-12-01 14:45           ` Jason Merrill
2014-12-03 12:01             ` Jakub Jelinek
2014-12-03 13:38               ` Jason Merrill
2014-12-03 17:03                 ` [PATCH] -fsanitize=vptr instrumentation (take 3) Jakub Jelinek
2015-01-15 17:23                   ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).