On Mon, Oct 27, 2014 at 05:16:05PM +0100, Jakub Jelinek wrote: > Here is an updated patch, ok if bootstrap/testing passes (so far just > checked with > make -j16 -k check RUNTESTFLAGS='--target_board=unix\{-m32,-m64\} asan.exp > tsan.exp ubsan.exp' > )?
Updated patch that passed bootstrap/regtest on i686-linux. Only minor changes: ensure during the cp_walk_tree we don't walk same trees more than once (with SAVE_EXPR resulted in huge compile times), tiny test cleanups, and making sure __ubsan_vptr_type_cache decl has normal visibility and is not considered local in shared libraries. 2014-10-28 Jakub Jelinek <ja...@redhat.com> * flag-types.h (enum sanitize_code): Add SANITIZE_VPTR, include SANITIZE_VPTR in SANITIZE_UNDEFINED. * opts.c (common_handle_option): Add -fsanitize=vptr. * sanitizer.def (BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS, BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT): New. * ubsan.h (enum ubsan_null_ckind): Add UBSAN_DOWNCAST_POINTER, UBSAN_DOWNCAST_REFERENCE, UBSAN_UPCAST and UBSAN_CAST_TO_VBASE. cp/ * config-lang.in (gtfiles): Add cp/cp-ubsan.c. * cp-gimplify.c (cp_genericize_r): Call cp_ubsan_maybe_instrument_member_call for member calls and cp_ubsan_maybe_instrument_member_access for member accesses. (struct cp_ubsan_check_member_access_data): New type. (cp_ubsan_check_member_access_r): New function. (cp_genericize_tree): Call cp_walk_tree with cp_ubsan_check_member_access_r callback for -fsanitize=vptr. * cp-tree.h (cp_ubsan_maybe_instrument_member_call, cp_ubsan_maybe_instrument_member_access, cp_ubsan_maybe_instrument_downcast, cp_ubsan_maybe_instrument_cast_to_vbase, cp_ubsan_fixup_downcast_instrumentation): New prototypes. * cp-ubsan.c: New file. * Make-lang.in (CXX_AND_OBJCXX_OBJS): Add cp/cp-ubsan.o. * constexpr.c (cxx_eval_call_expression): Handle 4 argument BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS{,_ABORT}. * typeck.c (build_class_member_access_expr): Provide locus for COMPONENT_REFs. (build_static_cast_1): Instrument downcasts. * class.c (build_base_path): For -fsanitize=vptr and !fixed_type_p add ubsan instrumentation for virtual_access. gcc/testsuite/ * g++.dg/ubsan/vptr-1.C: New test. * g++.dg/ubsan/vptr-2.C: New test. * g++.dg/ubsan/vptr-3.C: New test. * g++.dg/ubsan/vptr-4.C: New test. * g++.dg/ubsan/vptr-5.C: New test. * g++.dg/ubsan/vptr-6.C: New test. * g++.dg/ubsan/vptr-7.C: New test. * g++.dg/ubsan/vptr-8.C: New test. * g++.dg/ubsan/vptr-9.C: New test. libsanitizer/ * ubsan/ubsan_handlers.cc (__ubsan::TypeCheckKinds): Cherry pick upstream r219642. --- gcc/opts.c.jj 2014-10-25 20:46:06.443427738 +0200 +++ gcc/opts.c 2014-10-27 10:48:06.855097104 +0100 @@ -1575,6 +1575,7 @@ common_handle_option (struct gcc_options sizeof "returns-nonnull-attribute" - 1 }, { "object-size", SANITIZE_OBJECT_SIZE, sizeof "object-size" - 1 }, + { "vptr", SANITIZE_VPTR, sizeof "vptr" - 1 }, { NULL, 0, 0 } }; const char *comma; --- gcc/ubsan.h.jj 2014-10-25 20:46:06.460427425 +0200 +++ gcc/ubsan.h 2014-10-27 12:09:47.738576202 +0100 @@ -28,7 +28,11 @@ 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, + UBSAN_UPCAST, + UBSAN_CAST_TO_VBASE }; /* This controls how ubsan prints types. Used in ubsan_type_descriptor. */ --- gcc/sanitizer.def.jj 2014-10-25 20:46:06.374429010 +0200 +++ gcc/sanitizer.def 2014-10-27 13:54:41.087270591 +0100 @@ -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_NOTHROW_LEAF_LIST) --- gcc/cp/cp-tree.h.jj 2014-10-25 20:46:05.433446353 +0200 +++ gcc/cp/cp-tree.h 2014-10-27 12:41:59.263503080 +0100 @@ -6333,6 +6333,13 @@ extern vec<tree> cx_error_context /* 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 tree cp_ubsan_maybe_instrument_cast_to_vbase (location_t, tree, tree); +extern void cp_ubsan_fixup_downcast_instrumentation (tree *); + /* -- end of C++ */ #endif /* ! GCC_CP_TREE_H */ --- gcc/cp/cp-gimplify.c.jj 2014-10-25 20:46:05.484445413 +0200 +++ gcc/cp/cp-gimplify.c 2014-10-27 23:35:31.809141262 +0100 @@ -1193,9 +1193,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) @@ -1210,7 +1212,26 @@ 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; + } } } } @@ -1220,6 +1241,72 @@ cp_genericize_r (tree *stmt_p, int *walk return NULL; } +/* Data passed to cp_ubsan_check_member_access_r. */ + +struct cp_ubsan_check_member_access_data +{ + hash_set<tree> *pset; + bool is_addr; +}; + +/* Attempt to instrument member accesses inside of the function. */ + +static tree +cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data) +{ + tree stmt = *stmt_p, t; + struct cp_ubsan_check_member_access_data *ucmd + = (struct cp_ubsan_check_member_access_data *) data; + switch (TREE_CODE (stmt)) + { + case ADDR_EXPR: + t = TREE_OPERAND (stmt, 0); + while ((TREE_CODE (t) == MEM_REF || TREE_CODE (t) == INDIRECT_REF) + && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR) + t = TREE_OPERAND (TREE_OPERAND (t, 0), 0); + if (handled_component_p (t)) + { + *walk_subtrees = 0; + ucmd->is_addr = true; + cp_walk_tree (&t, cp_ubsan_check_member_access_r, + data, ucmd->pset); + ucmd->is_addr = false; + } + break; + case MEM_REF: + case INDIRECT_REF: + t = TREE_OPERAND (stmt, 0); + if (TREE_CODE (t) == ADDR_EXPR) + { + *walk_subtrees = 0; + t = TREE_OPERAND (stmt, 0); + cp_walk_tree (&t, cp_ubsan_check_member_access_r, data, ucmd->pset); + } + break; + case COMPONENT_REF: + if (!ucmd->is_addr) + cp_ubsan_maybe_instrument_member_access (stmt); + /* FALLTHRU */ + default: + if (ucmd->is_addr && handled_component_p (stmt)) + { + int i, len = TREE_OPERAND_LENGTH (stmt); + *walk_subtrees = 0; + if (!handled_component_p (TREE_OPERAND (stmt, 0))) + ucmd->is_addr = false; + for (i = 0; i < len; i++) + { + cp_walk_tree (&TREE_OPERAND (stmt, i), + cp_ubsan_check_member_access_r, data, ucmd->pset); + ucmd->is_addr = false; + } + ucmd->is_addr = true; + } + break; + } + return NULL_TREE; +} + /* Lower C++ front end trees to GENERIC in T_P. */ static void @@ -1233,6 +1320,14 @@ cp_genericize_tree (tree* t_p) cp_walk_tree (t_p, cp_genericize_r, &wtd, NULL); delete wtd.p_set; wtd.bind_expr_stack.release (); + if (flag_sanitize & SANITIZE_VPTR) + { + hash_set<tree> pset; + struct cp_ubsan_check_member_access_data ucmd; + ucmd.pset = &pset; + ucmd.is_addr = false; + cp_walk_tree (t_p, cp_ubsan_check_member_access_r, &ucmd, &pset); + } } /* If a function that should end with a return in non-void --- gcc/cp/class.c.jj 2014-10-13 10:09:24.000000000 +0200 +++ gcc/cp/class.c 2014-10-27 12:55:55.238445804 +0100 @@ -430,10 +430,20 @@ build_base_path (enum tree_code code, v_offset = cp_build_indirect_ref (v_offset, RO_NULL, complain); } else - v_offset = build_vfield_ref (cp_build_indirect_ref (expr, RO_NULL, - complain), - TREE_TYPE (TREE_TYPE (expr))); - + { + tree t = expr; + if ((flag_sanitize & SANITIZE_VPTR) && fixed_type_p == 0) + { + t = cp_ubsan_maybe_instrument_cast_to_vbase (input_location, + probe, expr); + if (t == NULL_TREE) + t = expr; + } + v_offset = build_vfield_ref (cp_build_indirect_ref (t, RO_NULL, + complain), + TREE_TYPE (TREE_TYPE (expr))); + } + if (v_offset == error_mark_node) return error_mark_node; --- gcc/cp/constexpr.c.jj 2014-10-25 20:46:05.473445616 +0200 +++ gcc/cp/constexpr.c 2014-10-27 10:48:06.860097006 +0100 @@ -1137,8 +1137,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 & SANITIZE_VPTR) + ? 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) --- gcc/cp/Make-lang.in.jj 2014-10-25 20:46:05.467445726 +0200 +++ gcc/cp/Make-lang.in 2014-10-27 10:48:06.860097006 +0100 @@ -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 cp/constexpr.o $(CXX_C_OBJS) + cp/vtable-class-hierarchy.o cp/constexpr.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) --- gcc/cp/cp-ubsan.c.jj 2014-10-27 10:48:06.861096987 +0100 +++ gcc/cp/cp-ubsan.c 2014-10-27 14:15:58.599404060 +0100 @@ -0,0 +1,294 @@ +/* UndefinedBehaviorSanitizer, undefined behavior detector. + Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Jakub Jelinek <ja...@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); + 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 + && 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; + + 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); + 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); + /* 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 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 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); + 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_PUBLIC (array) = 1; + TREE_STATIC (array) = 1; + DECL_EXTERNAL (array) = 1; + DECL_VISIBILITY (array) = VISIBILITY_DEFAULT; + DECL_VISIBILITY_SPECIFIED (array) = 1; + layout_decl (array, 0); + ubsan_vptr_type_cache_decl = array; + } + tree ti_decl = get_tinfo_decl (type); + mark_used (ti_decl); + tree data + = ubsan_create_data ("__ubsan_vptr_data", 1, &loc, + ubsan_type_descriptor (type), NULL_TREE, + build_address (ti_decl), + 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 & SANITIZE_VPTR) + ? 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, and + UBSAN_CAST_TO_VBASE during build_base_path, emit there + only the call and add the guard when genericizing the call. */ + switch (ckind) + { + default: + 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); + break; + case UBSAN_DOWNCAST_POINTER: + case UBSAN_DOWNCAST_REFERENCE: + case UBSAN_CAST_TO_VBASE: + 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; + break; + } + 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); +} + +/* Instrument cast to virtual base. */ + +tree +cp_ubsan_maybe_instrument_cast_to_vbase (location_t loc, tree type, tree op) +{ + return cp_ubsan_maybe_instrument_vptr (loc, op, type, true, + UBSAN_CAST_TO_VBASE); +} + +/* 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 & SANITIZE_VPTR) + ? 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" --- gcc/cp/typeck.c.jj 2014-10-25 20:46:05.448446076 +0200 +++ gcc/cp/typeck.c 2014-10-27 10:48:06.863096948 +0100 @@ -2424,8 +2424,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 @@ -6503,11 +6503,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++. @@ -6635,7 +6645,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)) --- gcc/cp/config-lang.in.jj 2014-10-25 20:46:05.485445394 +0200 +++ gcc/cp/config-lang.in 2014-10-27 10:48:06.863096948 +0100 @@ -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 \$(srcdir)/cp/constexpr.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/constexpr.c \$(srcdir)/cp/cp-ubsan.c" --- gcc/flag-types.h.jj 2014-10-25 20:46:05.494445228 +0200 +++ gcc/flag-types.h 2014-10-27 10:48:06.864096929 +0100 @@ -237,13 +237,14 @@ enum sanitize_code { SANITIZE_NONNULL_ATTRIBUTE = 1UL << 18, SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1UL << 19, SANITIZE_OBJECT_SIZE = 1UL << 20, + SANITIZE_VPTR = 1UL << 21, 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_OBJECT_SIZE, + | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR, SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST }; --- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj 2014-10-27 10:48:06.864096929 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-4.C 2014-10-27 10:48:06.864096929 +0100 @@ -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> (); --- gcc/testsuite/g++.dg/ubsan/vptr-5.C.jj 2014-10-27 10:48:06.864096929 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-5.C 2014-10-27 10:48:06.864096929 +0100 @@ -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 (); +} --- gcc/testsuite/g++.dg/ubsan/vptr-8.C.jj 2014-10-27 13:05:18.638094867 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-8.C 2014-10-27 16:26:58.213905581 +0100 @@ -0,0 +1,32 @@ +// { dg-do run } +// { dg-shouldfail "ubsan" } +// { dg-options "-fsanitize=vptr -fno-sanitize-recover=vptr" } + +extern "C" void abort (); + +struct S { virtual void f () {} }; +struct T : S { ~T (); }; +struct U : S { }; +struct V : T, virtual U {}; + +U *up; +V *vp; + +int +main () +{ + V v; + up = vp = &v; +} + +T::~T () +{ + if (vp != up) + abort (); +} + +// { dg-output "\[^\n\r]*vptr-8.C:24:\[0-9]*: runtime error: cast to virtual base of address 0x\[0-9a-fA-F]* which does not point to an object of type 'V'(\n|\r\n|\r)" } +// { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'T'(\n|\r\n|\r)" } +// { dg-output " ?.. .. .. .. ?.. .. .. .. ?.. .. .. .. \[^\n\r]*(\n|\r\n|\r)" } +// { dg-output " ?\\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" } +// { dg-output " ?vptr for 'T'\[^\n\r]*(\n|\r\n|\r)" } --- gcc/testsuite/g++.dg/ubsan/vptr-3.C.jj 2014-10-27 10:48:06.865096910 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-3.C 2014-10-27 10:48:06.865096910 +0100 @@ -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); +} --- gcc/testsuite/g++.dg/ubsan/vptr-7.C.jj 2014-10-27 10:48:06.865096910 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-7.C 2014-10-27 21:40:40.950984610 +0100 @@ -0,0 +1,26 @@ +// { dg-do compile } +// { dg-skip-if "" { *-*-* } { "-flto" } { "" } } +// { dg-options "-fsanitize=vptr -O2 -fdump-tree-optimized" } + +struct S { virtual ~S (); int i; }; + +int * +f1 (S *p) +{ + return &p->i; +} + +int * +f2 (S *p) +{ + return &*&p->i; +} + +int & +f3 (S *p) +{ + return p->i; +} + +// { dg-final { scan-tree-dump-times "__ubsan_handle_dynamic_type_cache_miss" 0 "optimized" } } +// { dg-final { cleanup-tree-dump "optimized" } } --- gcc/testsuite/g++.dg/ubsan/vptr-9.C.jj 2014-10-27 13:40:24.441196253 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-9.C 2014-10-27 16:26:13.384728254 +0100 @@ -0,0 +1,22 @@ +// { dg-do run } +// { dg-shouldfail "ubsan" } +// { dg-options "-fsanitize=vptr -fno-sanitize-recover=undefined" } + +struct S { virtual int f () { return 0; } }; +struct T : virtual S {}; +struct U { virtual int f () { return 0; } }; + +int +main () +{ + U u; + T *t = (T *) &u; + S *s = t; + return s->f (); +} + +// { dg-output "\[^\n\r]*vptr-9.C:14:\[0-9]*: runtime error: cast to virtual base 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)" } --- gcc/testsuite/g++.dg/ubsan/vptr-2.C.jj 2014-10-27 10:48:06.865096910 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-2.C 2014-10-27 10:48:06.865096910 +0100 @@ -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); +} --- gcc/testsuite/g++.dg/ubsan/vptr-6.C.jj 2014-10-27 10:48:06.866096890 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-6.C 2014-10-27 21:40:31.619148815 +0100 @@ -0,0 +1,32 @@ +// { dg-do compile } +// { dg-skip-if "" { *-*-* } { "-flto" } { "" } } +// { dg-options "-fsanitize=vptr -O2 -fdump-tree-optimized" } + +struct S { virtual ~S (); int i; _Complex int j[5]; }; + +int +f1 (S *p) +{ + return p->i; +} + +int +f2 (S *p) +{ + return *&p->i; +} + +_Complex int * +f3 (S *p, S *q) +{ + return &p->j[q->i]; +} + +int +f4 (S &p, S &q) +{ + return __imag__ p.j[q.i]; +} + +// { dg-final { scan-tree-dump-times "__ubsan_handle_dynamic_type_cache_miss" 5 "optimized" } } +// { dg-final { cleanup-tree-dump "optimized" } } --- gcc/testsuite/g++.dg/ubsan/vptr-1.C.jj 2014-10-27 10:48:06.866096890 +0100 +++ gcc/testsuite/g++.dg/ubsan/vptr-1.C 2014-10-27 10:48:06.866096890 +0100 @@ -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); +} --- libsanitizer/ubsan/ubsan_handlers.cc.jj 2014-09-24 11:08:04.184026152 +0200 +++ libsanitizer/ubsan/ubsan_handlers.cc 2014-10-27 12:22:34.151570646 +0100 @@ -28,10 +28,10 @@ static bool ignoreReport(SourceLocation } namespace __ubsan { - const char *TypeCheckKinds[] = { +const char *TypeCheckKinds[] = { "load of", "store to", "reference binding to", "member access within", - "member call on", "constructor call on", "downcast of", "downcast of" - }; + "member call on", "constructor call on", "downcast of", "downcast of", + "upcast of", "cast to virtual base of"}; } static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, Jakub