PR c++/70147 - handle primary virtual bases
authorJason Merrill <jason@redhat.com>
Fri, 18 Mar 2016 15:31:35 +0000 (11:31 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Fri, 18 Mar 2016 15:31:35 +0000 (11:31 -0400)
* class.c (vptr_via_virtual_p): New.
(most_primary_binfo): Factor out of build_rtti_vtbl_entries.
* cp-ubsan.c (cp_ubsan_dfs_initialize_vtbl_ptrs): Don't clear
a vptr from any virtual base in a not-in-charge 'structor.

From-SVN: r234335

gcc/cp/ChangeLog
gcc/cp/class.c
gcc/cp/cp-tree.h
gcc/cp/cp-ubsan.c
gcc/testsuite/g++.dg/ubsan/vptr-11.C [new file with mode: 0644]

index 0ee9f8b..9e1dbbb 100644 (file)
@@ -1,5 +1,11 @@
 2016-03-18  Jason Merrill  <jason@redhat.com>
 
+       PR c++/70147
+       * class.c (vptr_via_virtual_p): New.
+       (most_primary_binfo): Factor out of build_rtti_vtbl_entries.
+       * cp-ubsan.c (cp_ubsan_dfs_initialize_vtbl_ptrs): Don't clear
+       a vptr from any virtual base in a not-in-charge 'structor.
+
        * decl.c (build_clobber_this): Factor out of
        start_preparsed_function and begin_destructor_body.  Handle
        virtual bases better.
index 33e46e0..98cbab5 100644 (file)
@@ -8490,6 +8490,40 @@ get_primary_binfo (tree binfo)
   return copied_binfo (primary_base, binfo);
 }
 
+/* As above, but iterate until we reach the binfo that actually provides the
+   vptr for BINFO.  */
+
+static tree
+most_primary_binfo (tree binfo)
+{
+  tree b = binfo;
+  while (CLASSTYPE_HAS_PRIMARY_BASE_P (BINFO_TYPE (b))
+        && !BINFO_LOST_PRIMARY_P (b))
+    {
+      tree primary_base = get_primary_binfo (b);
+      gcc_assert (BINFO_PRIMARY_P (primary_base)
+                 && BINFO_INHERITANCE_CHAIN (primary_base) == b);
+      b = primary_base;
+    }
+  return b;
+}
+
+/* Returns true if BINFO gets its vptr from a virtual base of the most derived
+   type.  Note that the virtual inheritance might be above or below BINFO in
+   the hierarchy.  */
+
+bool
+vptr_via_virtual_p (tree binfo)
+{
+  if (TYPE_P (binfo))
+    binfo = TYPE_BINFO (binfo);
+  tree primary = most_primary_binfo (binfo);
+  /* Don't limit binfo_via_virtual, we want to return true when BINFO itself is
+     a morally virtual base.  */
+  tree virt = binfo_via_virtual (primary, NULL_TREE);
+  return virt != NULL_TREE;
+}
+
 /* If INDENTED_P is zero, indent to INDENT. Return nonzero.  */
 
 static int
@@ -9777,17 +9811,7 @@ build_rtti_vtbl_entries (tree binfo, vtbl_init_data* vid)
 
   /* To find the complete object, we will first convert to our most
      primary base, and then add the offset in the vtbl to that value.  */
-  b = binfo;
-  while (CLASSTYPE_HAS_PRIMARY_BASE_P (BINFO_TYPE (b))
-        && !BINFO_LOST_PRIMARY_P (b))
-    {
-      tree primary_base;
-
-      primary_base = get_primary_binfo (b);
-      gcc_assert (BINFO_PRIMARY_P (primary_base)
-                 && BINFO_INHERITANCE_CHAIN (primary_base) == b);
-      b = primary_base;
-    }
+  b = most_primary_binfo (binfo);
   offset = size_diffop_loc (input_location,
                        BINFO_OFFSET (vid->rtti_binfo), BINFO_OFFSET (b));
 
index 5cad141..6462d8a 100644 (file)
@@ -5677,6 +5677,7 @@ extern void invalidate_class_lookup_cache (void);
 extern void maybe_note_name_used_in_class      (tree, tree);
 extern void note_name_declared_in_class                (tree, tree);
 extern tree get_vtbl_decl_for_binfo            (tree);
+extern bool vptr_via_virtual_p                 (tree);
 extern void debug_class                                (tree);
 extern void debug_thunks                       (tree);
 extern void set_linkage_according_to_type      (tree, tree);
index 75aeeb8..be24a5c 100644 (file)
@@ -283,7 +283,7 @@ 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))
+  if (!BINFO_PRIMARY_P (binfo))
     {
       tree base_ptr = TREE_VALUE ((tree) data);
 
@@ -301,11 +301,10 @@ cp_ubsan_dfs_initialize_vtbl_ptrs (tree binfo, void *data)
       tree vtbl = build_zero_cst (TREE_TYPE (vtbl_ptr));
       tree stmt = cp_build_modify_expr (vtbl_ptr, NOP_EXPR, vtbl,
                                        tf_warning_or_error);
-      if (BINFO_VIRTUAL_P (binfo))
-       stmt = build3 (COND_EXPR, void_type_node,
-                      build2 (NE_EXPR, boolean_type_node,
-                              current_in_charge_parm, integer_zero_node),
-                      stmt, void_node);
+      if (vptr_via_virtual_p (binfo))
+       /* If this vptr comes from a virtual base of the complete object, only
+          clear it if we're in charge of virtual bases.  */
+       stmt = build_if_in_charge (stmt);
       finish_expr_stmt (stmt);
     }
 
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-11.C b/gcc/testsuite/g++.dg/ubsan/vptr-11.C
new file mode 100644 (file)
index 0000000..4516b1e
--- /dev/null
@@ -0,0 +1,84 @@
+// PR c++/70147
+// { dg-do run }
+// { dg-options "-fsanitize=vptr -fno-sanitize-recover=vptr" }
+
+static int ac, ad, bc, bd, cc, cd, dc, dd;
+struct A
+{
+  A ()
+  {
+    ac++;
+  }
+  virtual void f ()
+  {
+  }
+  __attribute__ ((noinline)) ~ A ();
+};
+
+struct D
+{
+  __attribute__ ((noinline)) D (int);
+  ~D ()
+  {
+    dd++;
+  }
+};
+struct B: virtual A, D
+{
+  B ():D (1)
+  {
+    bc++;
+  }
+  virtual void f ()
+  {
+  }
+  ~B ()
+  {
+    bd++;
+  }
+};
+
+struct C: B, virtual A
+{
+  C ()
+  {
+    cc++;
+  }
+  ~C ()
+  {
+    cd++;
+  }
+};
+
+D::D (int x)
+{
+  if (x)
+    throw 1;
+  dc++;
+}
+
+__attribute__ ((noinline, noclone))
+void foo (A * p)
+{
+  p->f ();
+}
+
+A::~A ()
+{
+  foo (this);
+  ad++;
+}
+
+int
+main ()
+{
+  try
+    {
+      C c;
+    }
+  catch ( ...)
+    {
+    }
+  if (ac != 1 || ad != 1 || bc || bd || cc || cd || dc || dd)
+    __builtin_abort ();
+}