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

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).