aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2016-03-18 11:31:35 -0400
committerJason Merrill <jason@gcc.gnu.org>2016-03-18 11:31:35 -0400
commitb5a28d803116ee10c88e0f762fdc427974b9b4fd (patch)
tree3ddc03dbda1ee4d2d03b3baf2c409380bbc15d4e /gcc
parentbf08acdaa570ae61e86b92660ddee07d20948919 (diff)
downloadgcc-b5a28d803116ee10c88e0f762fdc427974b9b4fd.zip
gcc-b5a28d803116ee10c88e0f762fdc427974b9b4fd.tar.gz
gcc-b5a28d803116ee10c88e0f762fdc427974b9b4fd.tar.bz2
PR c++/70147 - handle primary virtual bases
* 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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog6
-rw-r--r--gcc/cp/class.c46
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/cp-ubsan.c11
-rw-r--r--gcc/testsuite/g++.dg/ubsan/vptr-11.C84
5 files changed, 131 insertions, 17 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 0ee9f8b..9e1dbbb 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 33e46e0..98cbab5 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -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));
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5cad141..6462d8a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/cp-ubsan.c b/gcc/cp/cp-ubsan.c
index 75aeeb8..be24a5c 100644
--- a/gcc/cp/cp-ubsan.c
+++ b/gcc/cp/cp-ubsan.c
@@ -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
index 0000000..4516b1e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/vptr-11.C
@@ -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 ();
+}