This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[C++ PATCH] Improve -fsanitize=vptr (PR c++/70035)


Hi!

-fsanitize=vptr library side of instrumentation assumes that the vtable
pointers in objects are either NULL, or some vtable pointer, if it is random
garbage, it might crash.
The following patch attempts to clear the vtable pointers in objects next to
the spot where -flifetime-dse=2 clobbers the object.

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

2016-03-04  Jakub Jelinek  <jakub@redhat.com>

	PR c++/70035
	* cp-tree.h (cp_ubsan_maybe_initialize_vtbl_ptrs): New prototype.
	* decl.c (start_preparsed_function): Call
	cp_ubsan_maybe_initialize_vtbl_ptrs if needed.
	* cp-ubsan.c (cp_ubsan_dfs_initialize_vtbl_ptrs,
	cp_ubsan_maybe_initialize_vtbl_ptrs): New functions.

	* g++.dg/ubsan/pr70035.C: New test.

--- gcc/cp/cp-tree.h.jj	2016-02-26 08:57:10.000000000 +0100
+++ gcc/cp/cp-tree.h	2016-03-04 14:07:51.045471741 +0100
@@ -6940,6 +6940,7 @@ extern void cp_ubsan_maybe_instrument_me
 extern void cp_ubsan_instrument_member_accesses (tree *);
 extern tree cp_ubsan_maybe_instrument_downcast	(location_t, tree, tree, tree);
 extern tree cp_ubsan_maybe_instrument_cast_to_vbase (location_t, tree, tree);
+extern void cp_ubsan_maybe_initialize_vtbl_ptrs (tree);
 
 /* -- end of C++ */
 
--- gcc/cp/decl.c.jj	2016-02-24 22:33:35.000000000 +0100
+++ gcc/cp/decl.c	2016-03-04 14:53:54.283672876 +0100
@@ -14136,6 +14136,13 @@ start_preparsed_function (tree decl1, tr
       finish_expr_stmt (exprstmt);
     }
 
+  if (!processing_template_decl
+      && DECL_CONSTRUCTOR_P (decl1)
+      && (flag_sanitize & SANITIZE_VPTR)
+      && !DECL_CLONED_FUNCTION_P (decl1)
+      && !implicit_default_ctor_p (decl1))
+    cp_ubsan_maybe_initialize_vtbl_ptrs (current_class_ptr);
+
   return true;
 }
 
--- gcc/cp/cp-ubsan.c.jj	2016-02-24 23:00:44.000000000 +0100
+++ gcc/cp/cp-ubsan.c	2016-03-04 14:07:24.107840416 +0100
@@ -272,3 +272,55 @@ cp_ubsan_maybe_instrument_cast_to_vbase
   return cp_ubsan_maybe_instrument_vptr (loc, op, type, true,
 					 UBSAN_CAST_TO_VBASE);
 }
+
+/* Called from initialize_vtbl_ptrs via dfs_walk.  BINFO is the base
+   which we want to initialize the vtable pointer for, DATA is
+   TREE_LIST whose TREE_VALUE is the this ptr expression.  */
+
+static tree
+cp_ubsan_dfs_initialize_vtbl_ptrs (tree binfo, void *data)
+{
+  if (!TYPE_CONTAINS_VPTR_P (BINFO_TYPE (binfo)))
+    return dfs_skip_bases;
+
+  if (!BINFO_PRIMARY_P (binfo) || BINFO_VIRTUAL_P (binfo))
+    {
+      tree base_ptr = TREE_VALUE ((tree) data);
+
+      base_ptr = build_base_path (PLUS_EXPR, base_ptr, binfo, /*nonnull=*/1,
+				  tf_warning_or_error);
+
+      /* Compute the location of the vtpr.  */
+      tree vtbl_ptr
+	= build_vfield_ref (cp_build_indirect_ref (base_ptr, RO_NULL,
+						   tf_warning_or_error),
+			    TREE_TYPE (binfo));
+      gcc_assert (vtbl_ptr != error_mark_node);
+
+      /* Assign NULL to the vptr.  */
+      tree vtbl = build_zero_cst (TREE_TYPE (vtbl_ptr));
+      finish_expr_stmt (cp_build_modify_expr (vtbl_ptr, NOP_EXPR, vtbl,
+					      tf_warning_or_error));
+    }
+
+  return NULL_TREE;
+}
+
+/* Initialize all the vtable pointers in the object pointed to by
+   ADDR to NULL, so that we catch invalid calls to methods before
+   mem-initializers are completed.  */
+
+void
+cp_ubsan_maybe_initialize_vtbl_ptrs (tree addr)
+{
+  if (!cp_ubsan_instrument_vptr_p (NULL_TREE))
+    return;
+
+  tree type = TREE_TYPE (TREE_TYPE (addr));
+  tree list = build_tree_list (type, addr);
+
+  /* Walk through the hierarchy, initializing the vptr in each base
+     class to NULL.  */
+  dfs_walk_once (TYPE_BINFO (type), cp_ubsan_dfs_initialize_vtbl_ptrs,
+		 NULL, list);
+}
--- gcc/testsuite/g++.dg/ubsan/pr70035.C.jj	2016-03-04 14:20:39.234960689 +0100
+++ gcc/testsuite/g++.dg/ubsan/pr70035.C	2016-03-04 14:55:36.988268869 +0100
@@ -0,0 +1,26 @@
+// PR c++/70035
+// { dg-do run }
+// { dg-shouldfail "ubsan" }
+// { dg-options "-fsanitize=vptr -fno-sanitize-recover=undefined" }
+
+struct A {
+  A (int) {}
+  virtual int foo () { return 1; }
+};
+struct B : public A {
+  using A::foo;
+  B (int x) : A (foo (x)) {}
+  int foo (int x) { return x * 2; }
+};
+
+int
+main ()
+{
+  B b (20);
+}
+
+// { dg-output "\[^\n\r]*pr70035.C:12:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'B'(\n|\r\n|\r)" }
+// { dg-output "0x\[0-9a-fA-F]*: note: object has invalid vptr(\n|\r\n|\r)" }
+// { dg-output "  ?.. .. .. ..  ?.. .. .. ..  ?.. .. .. .. \[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output "              ?\\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output "              ?invalid vptr" }

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]