@@ -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;
@@ -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. */
@@ -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)
@@ -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 */
@@ -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);
@@ -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)
@@ -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)
@@ -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"
@@ -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))
@@ -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"
@@ -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
};
@@ -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> ();
@@ -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 ();
+}
@@ -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);
+}
@@ -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);
+}
@@ -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);
+}