diff options
author | Jason Merrill <jason@redhat.com> | 2016-03-18 11:31:35 -0400 |
---|---|---|
committer | Jason Merrill <jason@gcc.gnu.org> | 2016-03-18 11:31:35 -0400 |
commit | b5a28d803116ee10c88e0f762fdc427974b9b4fd (patch) | |
tree | 3ddc03dbda1ee4d2d03b3baf2c409380bbc15d4e /gcc | |
parent | bf08acdaa570ae61e86b92660ddee07d20948919 (diff) | |
download | gcc-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/ChangeLog | 6 | ||||
-rw-r--r-- | gcc/cp/class.c | 46 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/cp-ubsan.c | 11 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ubsan/vptr-11.C | 84 |
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 (); +} |