Commit 35228ac7 by Jakub Jelinek Committed by Jakub Jelinek

flag-types.h (enum sanitize_code): Add SANITIZE_VPTR, include SANITIZE_VPTR in SANITIZE_UNDEFINED.

	* 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.
	(ubsan_expand_vptr_ifn): New prototype.
	* internal-fn.c (expand_ANNOTATE, expand_GOMP_SIMD_LANE,
	expand_GOMP_SIMD_VF, expand_GOMP_SIMD_LAST_LANE, expand_UBSAN_NULL,
	expand_UBSAN_BOUNDS, expand_UBSAN_OBJECT_SIZE, expand_ASAN_CHECK,
	expand_LOOP_VECTORIZED): Make argument nameless, remove
	ATTRIBUTE_UNUSED.
	(expand_UBSAN_VPTR): New function.
	* internal-fn.def (UBSAN_NULL, ASAN_CHECK): Use R instead of W
	in fn spec.
	(UBSAN_VPTR): New internal function.
	* sanopt.c (tree_map_traits): Renamed to ...
	(sanopt_tree_map_traits): ... this.
	(sanopt_tree_triplet, sanopt_tree_triplet_map_traits): New classes.
	(sanopt_ctx): Adjust asan_check_map type for tree_map_traits
	to sanopt_tree_map_traits renaming.  Add vptr_check_map field.
	(maybe_optimize_ubsan_vptr_ifn): New function.
	(sanopt_optimize_walker): Handle IFN_UBSAN_VPTR.
	(pass_sanopt::execute): Likewise.  Call sanopt_optimize even for
	-fsanitize=vptr.
	* tree-ssa-alias.c (call_may_clobber_ref_p_1): Handle certain
	internal calls like pure functions for aliasing, even when they
	have other side-effects that prevent making them ECF_PURE.
	* ubsan.c (ubsan_vptr_type_cache_decl): New variable.
	(ubsan_expand_vptr_ifn): New function.
cp/
	* cp-gimplify.c (cp_genericize_r): Call
	cp_ubsan_maybe_instrument_member_call for member calls.
	(cp_ubsan_check_member_access_r): New function.
	(cp_genericize_tree): Call cp_ubsan_instrument_member_accesses.
	* cp-tree.h (cp_ubsan_maybe_instrument_member_call,
	cp_ubsan_instrument_member_accesses,
	cp_ubsan_maybe_instrument_downcast,
	cp_ubsan_maybe_instrument_cast_to_vbase): 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): Return void_node
	for IFN_UBSAN_VPTR.
	(potential_constant_expression_1): Return true for
	UBSAN_NULL, UBSAN_BOUNDS and UBSAN_VPTR internal calls.
	* 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.
	* call.c: Include internal-fn.h.
	(set_flags_from_callee): Handle internal calls.
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.

From-SVN: r219695
parent 9f584046
2015-01-15 Jakub Jelinek <jakub@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.
(ubsan_expand_vptr_ifn): New prototype.
* internal-fn.c (expand_ANNOTATE, expand_GOMP_SIMD_LANE,
expand_GOMP_SIMD_VF, expand_GOMP_SIMD_LAST_LANE, expand_UBSAN_NULL,
expand_UBSAN_BOUNDS, expand_UBSAN_OBJECT_SIZE, expand_ASAN_CHECK,
expand_LOOP_VECTORIZED): Make argument nameless, remove
ATTRIBUTE_UNUSED.
(expand_UBSAN_VPTR): New function.
* internal-fn.def (UBSAN_NULL, ASAN_CHECK): Use R instead of W
in fn spec.
(UBSAN_VPTR): New internal function.
* sanopt.c (tree_map_traits): Renamed to ...
(sanopt_tree_map_traits): ... this.
(sanopt_tree_triplet, sanopt_tree_triplet_map_traits): New classes.
(sanopt_ctx): Adjust asan_check_map type for tree_map_traits
to sanopt_tree_map_traits renaming. Add vptr_check_map field.
(maybe_optimize_ubsan_vptr_ifn): New function.
(sanopt_optimize_walker): Handle IFN_UBSAN_VPTR.
(pass_sanopt::execute): Likewise. Call sanopt_optimize even for
-fsanitize=vptr.
* tree-ssa-alias.c (call_may_clobber_ref_p_1): Handle certain
internal calls like pure functions for aliasing, even when they
have other side-effects that prevent making them ECF_PURE.
* ubsan.c (ubsan_vptr_type_cache_decl): New variable.
(ubsan_expand_vptr_ifn): New function.
2015-01-15 Vladimir Makarov <vmakarov@redhat.com> 2015-01-15 Vladimir Makarov <vmakarov@redhat.com>
PR rtl-optimization/64110 PR rtl-optimization/64110
......
2015-01-15 Jakub Jelinek <jakub@redhat.com>
* cp-gimplify.c (cp_genericize_r): Call
cp_ubsan_maybe_instrument_member_call for member calls.
(cp_ubsan_check_member_access_r): New function.
(cp_genericize_tree): Call cp_ubsan_instrument_member_accesses.
* cp-tree.h (cp_ubsan_maybe_instrument_member_call,
cp_ubsan_instrument_member_accesses,
cp_ubsan_maybe_instrument_downcast,
cp_ubsan_maybe_instrument_cast_to_vbase): 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): Return void_node
for IFN_UBSAN_VPTR.
(potential_constant_expression_1): Return true for
UBSAN_NULL, UBSAN_BOUNDS and UBSAN_VPTR internal calls.
* 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.
* call.c: Include internal-fn.h.
(set_flags_from_callee): Handle internal calls.
2015-01-15 Momchil Velikov <momchil.velikov@gmail.com> 2015-01-15 Momchil Velikov <momchil.velikov@gmail.com>
PR c++/59366 PR c++/59366
......
...@@ -78,7 +78,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \ ...@@ -78,7 +78,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \
cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \ cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \
cp/cp-cilkplus.o \ cp/cp-cilkplus.o \
cp/cp-gimplify.o cp/cp-array-notation.o cp/lambda.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++. # Language-specific object files for C++.
CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS) CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS)
......
...@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see
#include "ipa-ref.h" #include "ipa-ref.h"
#include "cgraph.h" #include "cgraph.h"
#include "wide-int.h" #include "wide-int.h"
#include "internal-fn.h"
/* The various kinds of conversion. */ /* The various kinds of conversion. */
...@@ -338,13 +339,16 @@ build_call_n (tree function, int n, ...) ...@@ -338,13 +339,16 @@ build_call_n (tree function, int n, ...)
void void
set_flags_from_callee (tree call) set_flags_from_callee (tree call)
{ {
int nothrow; bool nothrow;
tree decl = get_callee_fndecl (call); tree decl = get_callee_fndecl (call);
/* We check both the decl and the type; a function may be known not to /* We check both the decl and the type; a function may be known not to
throw without being declared throw(). */ throw without being declared throw(). */
nothrow = ((decl && TREE_NOTHROW (decl)) nothrow = decl && TREE_NOTHROW (decl);
|| TYPE_NOTHROW_P (TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (call))))); if (CALL_EXPR_FN (call))
nothrow |= TYPE_NOTHROW_P (TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (call))));
else if (internal_fn_flags (CALL_EXPR_IFN (call)) & ECF_NOTHROW)
nothrow = true;
if (!nothrow && at_function_scope_p () && cfun && cp_function_chain) if (!nothrow && at_function_scope_p () && cfun && cp_function_chain)
cp_function_chain->can_throw = 1; cp_function_chain->can_throw = 1;
......
...@@ -447,10 +447,20 @@ build_base_path (enum tree_code code, ...@@ -447,10 +447,20 @@ build_base_path (enum tree_code code,
v_offset = cp_build_indirect_ref (v_offset, RO_NULL, complain); v_offset = cp_build_indirect_ref (v_offset, RO_NULL, complain);
} }
else else
v_offset = build_vfield_ref (cp_build_indirect_ref (expr, RO_NULL, {
complain), tree t = expr;
TREE_TYPE (TREE_TYPE (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) if (v_offset == error_mark_node)
return error_mark_node; return error_mark_node;
......
...@@ -1176,6 +1176,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, ...@@ -1176,6 +1176,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
{ {
case IFN_UBSAN_NULL: case IFN_UBSAN_NULL:
case IFN_UBSAN_BOUNDS: case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
return void_node; return void_node;
default: default:
if (!ctx->quiet) if (!ctx->quiet)
...@@ -3820,6 +3821,19 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, ...@@ -3820,6 +3821,19 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
if (fun == NULL_TREE) if (fun == NULL_TREE)
{ {
if (TREE_CODE (t) == CALL_EXPR
&& CALL_EXPR_FN (t) == NULL_TREE)
switch (CALL_EXPR_IFN (t))
{
/* These should be ignored, they are optimized away from
constexpr functions. */
case IFN_UBSAN_NULL:
case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
return true;
default:
break;
}
/* fold_call_expr can't do anything with IFN calls. */ /* fold_call_expr can't do anything with IFN calls. */
if (flags & tf_error) if (flags & tf_error)
error_at (EXPR_LOC_OR_LOC (t, input_location), error_at (EXPR_LOC_OR_LOC (t, input_location),
......
...@@ -1197,9 +1197,11 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) ...@@ -1197,9 +1197,11 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
*stmt_p = size_one_node; *stmt_p = size_one_node;
return NULL; 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) && TREE_CODE (TREE_TYPE (stmt)) == REFERENCE_TYPE)
ubsan_maybe_instrument_reference (stmt); ubsan_maybe_instrument_reference (stmt);
else if (TREE_CODE (stmt) == CALL_EXPR) else if (TREE_CODE (stmt) == CALL_EXPR)
...@@ -1214,7 +1216,10 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) ...@@ -1214,7 +1216,10 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
= TREE_CODE (fn) == ADDR_EXPR = TREE_CODE (fn) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
&& DECL_CONSTRUCTOR_P (TREE_OPERAND (fn, 0)); && 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);
} }
} }
} }
...@@ -1237,6 +1242,8 @@ cp_genericize_tree (tree* t_p) ...@@ -1237,6 +1242,8 @@ cp_genericize_tree (tree* t_p)
cp_walk_tree (t_p, cp_genericize_r, &wtd, NULL); cp_walk_tree (t_p, cp_genericize_r, &wtd, NULL);
delete wtd.p_set; delete wtd.p_set;
wtd.bind_expr_stack.release (); wtd.bind_expr_stack.release ();
if (flag_sanitize & SANITIZE_VPTR)
cp_ubsan_instrument_member_accesses (t_p);
} }
/* If a function that should end with a return in non-void /* If a function that should end with a return in non-void
......
...@@ -6412,6 +6412,12 @@ extern vec<tree> cx_error_context (void); ...@@ -6412,6 +6412,12 @@ extern vec<tree> cx_error_context (void);
/* In c-family/cilk.c */ /* In c-family/cilk.c */
extern bool cilk_valid_spawn (tree); extern bool cilk_valid_spawn (tree);
/* In cp-ubsan.c */
extern void cp_ubsan_maybe_instrument_member_call (tree);
extern void cp_ubsan_instrument_member_accesses (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);
/* -- end of C++ */ /* -- end of C++ */
#endif /* ! GCC_CP_TREE_H */ #endif /* ! GCC_CP_TREE_H */
/* 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 "hash-set.h"
#include "machmode.h"
#include "vec.h"
#include "double-int.h"
#include "input.h"
#include "alias.h"
#include "symtab.h"
#include "options.h"
#include "wide-int.h"
#include "inchash.h"
#include "tree.h"
#include "alloc-pool.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"
#include "is-a.h"
#include "predict.h"
#include "tree-ssa-alias.h"
#include "basic-block.h"
#include "gimple-expr.h"
#include "gimple.h"
#include "ipa-ref.h"
#include "lto-streamer.h"
#include "cgraph.h"
/* Test if we should instrument vptr access. */
static bool
cp_ubsan_instrument_vptr_p (tree type)
{
if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
return false;
if (current_function_decl
&& lookup_attribute ("no_sanitize_undefined",
DECL_ATTRIBUTES (current_function_decl)))
return false;
if (type)
{
type = TYPE_MAIN_VARIANT (type);
if (!CLASS_TYPE_P (type) || !CLASSTYPE_VTABLES (type))
return false;
}
return true;
}
/* Helper function for
cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
Instrument vptr access. */
static tree
cp_ubsan_instrument_vptr (location_t loc, tree op, tree type, bool is_addr,
enum ubsan_null_ckind ckind)
{
type = TYPE_MAIN_VARIANT (type);
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);
if (ckind == UBSAN_DOWNCAST_POINTER)
vptr = fold_build3 (COND_EXPR, uint64_type_node,
fold_build2 (NE_EXPR, boolean_type_node, op,
build_zero_cst (TREE_TYPE (op))),
vptr, build_int_cst (uint64_type_node, 0));
vptr = build1_loc (loc, SAVE_EXPR, uint64_type_node, vptr);
tree ti_decl = get_tinfo_decl (type);
mark_used (ti_decl);
tree ptype = build_pointer_type (type);
tree call
= build_call_expr_internal_loc (loc, IFN_UBSAN_VPTR,
void_type_node, 5, op, vptr, str_hash,
build_address (ti_decl),
build_int_cst (ptype, ckind));
TREE_SIDE_EFFECTS (call) = 1;
return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op);
}
/* Helper function for
cp_ubsan_maybe_instrument_{member_{call,access},downcast}.
Instrument vptr access if it should be instrumented, otherwise return
NULL_TREE. */
static tree
cp_ubsan_maybe_instrument_vptr (location_t loc, tree op, tree type,
bool is_addr, enum ubsan_null_ckind ckind)
{
if (!cp_ubsan_instrument_vptr_p (type))
return NULL_TREE;
return cp_ubsan_instrument_vptr (loc, op, type, is_addr, ckind);
}
/* 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;
}
/* Data passed to cp_ubsan_check_member_access_r. */
struct cp_ubsan_check_member_access_data
{
hash_set<tree> *pset;
bool is_addr;
};
static tree cp_ubsan_check_member_access_r (tree *, int *, void *);
/* Instrument a member access. */
static bool
cp_ubsan_maybe_instrument_member_access
(tree stmt, cp_ubsan_check_member_access_data *ucmd)
{
if (DECL_ARTIFICIAL (TREE_OPERAND (stmt, 1)))
return false;
tree base = TREE_OPERAND (stmt, 0);
if (!cp_ubsan_instrument_vptr_p (TREE_TYPE (base)))
return false;
cp_walk_tree (&base, cp_ubsan_check_member_access_r, ucmd, ucmd->pset);
base = cp_ubsan_instrument_vptr (EXPR_LOCATION (stmt), base,
TREE_TYPE (base), false,
UBSAN_MEMBER_ACCESS);
TREE_OPERAND (stmt, 0)
= build_fold_indirect_ref_loc (EXPR_LOCATION (stmt), base);
return true;
}
/* Attempt to instrument member accesses inside of the function.
cp_ubsan_maybe_instrument_member_access should be called on COMPONENT_REFs
in the GENERIC IL, but only when the field is actually accessed, not
merely when it's address is taken. Therefore we track in is_addr field
whether in the current context we are processing address taken
handled components or not. E.g. for &x->y[w->z] we want to call
cp_ubsan_maybe_instrument_member_access on *w.z COMPONENT_REF, but
not on *x.y. */
static tree
cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
{
tree stmt = *stmt_p, t;
cp_ubsan_check_member_access_data *ucmd
= (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, ucmd))
{
*walk_subtrees = 0;
break;
}
/* 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;
}
/* Instrument all member accesses inside GENERIC *T_P. */
void
cp_ubsan_instrument_member_accesses (tree *t_p)
{
if (cp_ubsan_instrument_vptr_p (NULL_TREE))
{
hash_set<tree> pset;
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);
}
}
/* 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);
}
...@@ -2425,8 +2425,8 @@ build_class_member_access_expr (tree object, tree member, ...@@ -2425,8 +2425,8 @@ build_class_member_access_expr (tree object, tree member,
member_type = cp_build_qualified_type (member_type, type_quals); member_type = cp_build_qualified_type (member_type, type_quals);
} }
result = build3 (COMPONENT_REF, member_type, object, member, result = build3_loc (input_location, COMPONENT_REF, member_type,
NULL_TREE); object, member, NULL_TREE);
result = fold_if_not_in_template (result); result = fold_if_not_in_template (result);
/* Mark the expression const or volatile, as appropriate. Even /* Mark the expression const or volatile, as appropriate. Even
...@@ -6474,11 +6474,21 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p, ...@@ -6474,11 +6474,21 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
base = lookup_base (TREE_TYPE (type), intype, base = lookup_base (TREE_TYPE (type), intype,
c_cast_p ? ba_unique : ba_check, c_cast_p ? ba_unique : ba_check,
NULL, complain); 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" /* Convert from "B*" to "D*". This function will check that "B"
is not a virtual base of "D". */ is not a virtual base of "D". */
expr = build_base_path (MINUS_EXPR, build_address (expr), expr = build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false,
base, /*nonnull=*/false, complain); complain);
/* Convert the pointer to a reference -- but then remember that /* Convert the pointer to a reference -- but then remember that
there are no expressions with reference type in C++. there are no expressions with reference type in C++.
...@@ -6606,7 +6616,16 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p, ...@@ -6606,7 +6616,16 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
NULL, complain); NULL, complain);
expr = build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false, expr = build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false,
complain); 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)) if ((TYPE_PTRDATAMEM_P (type) && TYPE_PTRDATAMEM_P (intype))
......
...@@ -237,13 +237,14 @@ enum sanitize_code { ...@@ -237,13 +237,14 @@ enum sanitize_code {
SANITIZE_NONNULL_ATTRIBUTE = 1UL << 18, SANITIZE_NONNULL_ATTRIBUTE = 1UL << 18,
SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1UL << 19, SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1UL << 19,
SANITIZE_OBJECT_SIZE = 1UL << 20, SANITIZE_OBJECT_SIZE = 1UL << 20,
SANITIZE_VPTR = 1UL << 21,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
| SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
| SANITIZE_BOUNDS | SANITIZE_ALIGNMENT | SANITIZE_BOUNDS | SANITIZE_ALIGNMENT
| SANITIZE_NONNULL_ATTRIBUTE | SANITIZE_NONNULL_ATTRIBUTE
| SANITIZE_RETURNS_NONNULL_ATTRIBUTE | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
| SANITIZE_OBJECT_SIZE, | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
}; };
......
...@@ -166,7 +166,7 @@ expand_STORE_LANES (gcall *stmt) ...@@ -166,7 +166,7 @@ expand_STORE_LANES (gcall *stmt)
} }
static void static void
expand_ANNOTATE (gcall *stmt ATTRIBUTE_UNUSED) expand_ANNOTATE (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -174,7 +174,7 @@ expand_ANNOTATE (gcall *stmt ATTRIBUTE_UNUSED) ...@@ -174,7 +174,7 @@ expand_ANNOTATE (gcall *stmt ATTRIBUTE_UNUSED)
/* This should get expanded in adjust_simduid_builtins. */ /* This should get expanded in adjust_simduid_builtins. */
static void static void
expand_GOMP_SIMD_LANE (gcall *stmt ATTRIBUTE_UNUSED) expand_GOMP_SIMD_LANE (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -182,7 +182,7 @@ expand_GOMP_SIMD_LANE (gcall *stmt ATTRIBUTE_UNUSED) ...@@ -182,7 +182,7 @@ expand_GOMP_SIMD_LANE (gcall *stmt ATTRIBUTE_UNUSED)
/* This should get expanded in adjust_simduid_builtins. */ /* This should get expanded in adjust_simduid_builtins. */
static void static void
expand_GOMP_SIMD_VF (gcall *stmt ATTRIBUTE_UNUSED) expand_GOMP_SIMD_VF (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -190,7 +190,7 @@ expand_GOMP_SIMD_VF (gcall *stmt ATTRIBUTE_UNUSED) ...@@ -190,7 +190,7 @@ expand_GOMP_SIMD_VF (gcall *stmt ATTRIBUTE_UNUSED)
/* This should get expanded in adjust_simduid_builtins. */ /* This should get expanded in adjust_simduid_builtins. */
static void static void
expand_GOMP_SIMD_LAST_LANE (gcall *stmt ATTRIBUTE_UNUSED) expand_GOMP_SIMD_LAST_LANE (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -198,7 +198,7 @@ expand_GOMP_SIMD_LAST_LANE (gcall *stmt ATTRIBUTE_UNUSED) ...@@ -198,7 +198,7 @@ expand_GOMP_SIMD_LAST_LANE (gcall *stmt ATTRIBUTE_UNUSED)
/* This should get expanded in the sanopt pass. */ /* This should get expanded in the sanopt pass. */
static void static void
expand_UBSAN_NULL (gcall *stmt ATTRIBUTE_UNUSED) expand_UBSAN_NULL (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -206,7 +206,7 @@ expand_UBSAN_NULL (gcall *stmt ATTRIBUTE_UNUSED) ...@@ -206,7 +206,7 @@ expand_UBSAN_NULL (gcall *stmt ATTRIBUTE_UNUSED)
/* This should get expanded in the sanopt pass. */ /* This should get expanded in the sanopt pass. */
static void static void
expand_UBSAN_BOUNDS (gcall *stmt ATTRIBUTE_UNUSED) expand_UBSAN_BOUNDS (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -214,7 +214,7 @@ expand_UBSAN_BOUNDS (gcall *stmt ATTRIBUTE_UNUSED) ...@@ -214,7 +214,7 @@ expand_UBSAN_BOUNDS (gcall *stmt ATTRIBUTE_UNUSED)
/* This should get expanded in the sanopt pass. */ /* This should get expanded in the sanopt pass. */
static void static void
expand_UBSAN_OBJECT_SIZE (gcall *stmt ATTRIBUTE_UNUSED) expand_UBSAN_VPTR (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -222,7 +222,15 @@ expand_UBSAN_OBJECT_SIZE (gcall *stmt ATTRIBUTE_UNUSED) ...@@ -222,7 +222,15 @@ expand_UBSAN_OBJECT_SIZE (gcall *stmt ATTRIBUTE_UNUSED)
/* This should get expanded in the sanopt pass. */ /* This should get expanded in the sanopt pass. */
static void static void
expand_ASAN_CHECK (gcall *stmt ATTRIBUTE_UNUSED) expand_UBSAN_OBJECT_SIZE (gcall *)
{
gcc_unreachable ();
}
/* This should get expanded in the sanopt pass. */
static void
expand_ASAN_CHECK (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -1889,7 +1897,7 @@ expand_MUL_OVERFLOW (gcall *stmt) ...@@ -1889,7 +1897,7 @@ expand_MUL_OVERFLOW (gcall *stmt)
/* This should get folded in tree-vectorizer.c. */ /* This should get folded in tree-vectorizer.c. */
static void static void
expand_LOOP_VECTORIZED (gcall *stmt ATTRIBUTE_UNUSED) expand_LOOP_VECTORIZED (gcall *)
{ {
gcc_unreachable (); gcc_unreachable ();
} }
......
...@@ -48,15 +48,16 @@ DEF_INTERNAL_FN (LOOP_VECTORIZED, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL) ...@@ -48,15 +48,16 @@ DEF_INTERNAL_FN (LOOP_VECTORIZED, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF, NULL) DEF_INTERNAL_FN (MASK_LOAD, ECF_PURE | ECF_LEAF, NULL)
DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF, NULL) DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF, NULL)
DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW, ".W.") DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW, ".R.")
DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_VPTR, ECF_LEAF | ECF_NOTHROW, ".RR..")
DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL) DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W...") DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R...")
DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
......
...@@ -1588,6 +1588,7 @@ common_handle_option (struct gcc_options *opts, ...@@ -1588,6 +1588,7 @@ common_handle_option (struct gcc_options *opts,
sizeof "returns-nonnull-attribute" - 1 }, sizeof "returns-nonnull-attribute" - 1 },
{ "object-size", SANITIZE_OBJECT_SIZE, { "object-size", SANITIZE_OBJECT_SIZE,
sizeof "object-size" - 1 }, sizeof "object-size" - 1 },
{ "vptr", SANITIZE_VPTR, sizeof "vptr" - 1 },
{ "all", ~0, sizeof "all" - 1 }, { "all", ~0, sizeof "all" - 1 },
{ NULL, 0, 0 } { NULL, 0, 0 }
}; };
......
...@@ -499,3 +499,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT, ...@@ -499,3 +499,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT,
"__ubsan_handle_nonnull_return_abort", "__ubsan_handle_nonnull_return_abort",
BT_FN_VOID_PTR, BT_FN_VOID_PTR,
ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) 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)
...@@ -108,7 +108,7 @@ maybe_get_single_definition (tree t) ...@@ -108,7 +108,7 @@ maybe_get_single_definition (tree t)
/* Traits class for tree hash maps below. */ /* Traits class for tree hash maps below. */
struct tree_map_traits : default_hashmap_traits struct sanopt_tree_map_traits : default_hashmap_traits
{ {
static inline hashval_t hash (const_tree ref) static inline hashval_t hash (const_tree ref)
{ {
...@@ -121,6 +121,63 @@ struct tree_map_traits : default_hashmap_traits ...@@ -121,6 +121,63 @@ struct tree_map_traits : default_hashmap_traits
} }
}; };
/* Tree triplet for vptr_check_map. */
struct sanopt_tree_triplet
{
tree t1, t2, t3;
};
/* Traits class for tree triplet hash maps below. */
struct sanopt_tree_triplet_map_traits : default_hashmap_traits
{
static inline hashval_t
hash (const sanopt_tree_triplet &ref)
{
inchash::hash hstate (0);
inchash::add_expr (ref.t1, hstate);
inchash::add_expr (ref.t2, hstate);
inchash::add_expr (ref.t3, hstate);
return hstate.end ();
}
static inline bool
equal_keys (const sanopt_tree_triplet &ref1, const sanopt_tree_triplet &ref2)
{
return operand_equal_p (ref1.t1, ref2.t1, 0)
&& operand_equal_p (ref1.t2, ref2.t2, 0)
&& operand_equal_p (ref1.t3, ref2.t3, 0);
}
template<typename T>
static inline void
mark_deleted (T &e)
{
e.m_key.t1 = reinterpret_cast<T *> (1);
}
template<typename T>
static inline void
mark_empty (T &e)
{
e.m_key.t1 = NULL;
}
template<typename T>
static inline bool
is_deleted (T &e)
{
return e.m_key.t1 == (void *) 1;
}
template<typename T>
static inline bool
is_empty (T &e)
{
return e.m_key.t1 == NULL;
}
};
/* This is used to carry various hash maps and variables used /* This is used to carry various hash maps and variables used
in sanopt_optimize_walker. */ in sanopt_optimize_walker. */
...@@ -132,7 +189,13 @@ struct sanopt_ctx ...@@ -132,7 +189,13 @@ struct sanopt_ctx
/* This map maps a pointer (the second argument of ASAN_CHECK) to /* This map maps a pointer (the second argument of ASAN_CHECK) to
a vector of ASAN_CHECK call statements that check the access. */ a vector of ASAN_CHECK call statements that check the access. */
hash_map<tree, auto_vec<gimple>, tree_map_traits> asan_check_map; hash_map<tree, auto_vec<gimple>, sanopt_tree_map_traits> asan_check_map;
/* This map maps a tree triplet (the first, second and fourth argument
of UBSAN_VPTR) to a vector of UBSAN_VPTR call statements that check
that virtual table pointer. */
hash_map<sanopt_tree_triplet, auto_vec<gimple>,
sanopt_tree_triplet_map_traits> vptr_check_map;
/* Number of IFN_ASAN_CHECK statements. */ /* Number of IFN_ASAN_CHECK statements. */
int asan_num_accesses; int asan_num_accesses;
...@@ -306,6 +369,32 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt) ...@@ -306,6 +369,32 @@ maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
return remove; return remove;
} }
/* Optimize away redundant UBSAN_VPTR calls. The second argument
is the value loaded from the virtual table, so rely on FRE to find out
when we can actually optimize. */
static bool
maybe_optimize_ubsan_vptr_ifn (struct sanopt_ctx *ctx, gimple stmt)
{
gcc_assert (gimple_call_num_args (stmt) == 5);
sanopt_tree_triplet triplet;
triplet.t1 = gimple_call_arg (stmt, 0);
triplet.t2 = gimple_call_arg (stmt, 1);
triplet.t3 = gimple_call_arg (stmt, 3);
auto_vec<gimple> &v = ctx->vptr_check_map.get_or_insert (triplet);
gimple g = maybe_get_dominating_check (v);
if (!g)
{
/* For this PTR we don't have any UBSAN_VPTR stmts recorded, so there's
nothing to optimize yet. */
v.safe_push (stmt);
return false;
}
return true;
}
/* Returns TRUE if ASan check of length LEN in block BB can be removed /* Returns TRUE if ASan check of length LEN in block BB can be removed
if preceded by checks in V. */ if preceded by checks in V. */
...@@ -497,6 +586,9 @@ sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx) ...@@ -497,6 +586,9 @@ sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
case IFN_UBSAN_NULL: case IFN_UBSAN_NULL:
remove = maybe_optimize_ubsan_null_ifn (ctx, stmt); remove = maybe_optimize_ubsan_null_ifn (ctx, stmt);
break; break;
case IFN_UBSAN_VPTR:
remove = maybe_optimize_ubsan_vptr_ifn (ctx, stmt);
break;
case IFN_ASAN_CHECK: case IFN_ASAN_CHECK:
if (asan_check_optimize) if (asan_check_optimize)
remove = maybe_optimize_asan_check_ifn (ctx, stmt); remove = maybe_optimize_asan_check_ifn (ctx, stmt);
...@@ -601,7 +693,8 @@ pass_sanopt::execute (function *fun) ...@@ -601,7 +693,8 @@ pass_sanopt::execute (function *fun)
/* Try to remove redundant checks. */ /* Try to remove redundant checks. */
if (optimize if (optimize
&& (flag_sanitize && (flag_sanitize
& (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_ADDRESS))) & (SANITIZE_NULL | SANITIZE_ALIGNMENT
| SANITIZE_ADDRESS | SANITIZE_VPTR)))
asan_num_accesses = sanopt_optimize (fun); asan_num_accesses = sanopt_optimize (fun);
else if (flag_sanitize & SANITIZE_ADDRESS) else if (flag_sanitize & SANITIZE_ADDRESS)
{ {
...@@ -647,6 +740,9 @@ pass_sanopt::execute (function *fun) ...@@ -647,6 +740,9 @@ pass_sanopt::execute (function *fun)
case IFN_UBSAN_OBJECT_SIZE: case IFN_UBSAN_OBJECT_SIZE:
no_next = ubsan_expand_objsize_ifn (&gsi); no_next = ubsan_expand_objsize_ifn (&gsi);
break; break;
case IFN_UBSAN_VPTR:
no_next = ubsan_expand_vptr_ifn (&gsi);
break;
case IFN_ASAN_CHECK: case IFN_ASAN_CHECK:
no_next = asan_expand_check_ifn (&gsi, use_calls); no_next = asan_expand_check_ifn (&gsi, use_calls);
break; break;
......
2015-01-15 Jakub Jelinek <jakub@redhat.com>
* 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.
2015-01-15 Eric Botcazou <ebotcazou@adacore.com> 2015-01-15 Eric Botcazou <ebotcazou@adacore.com>
* lib/c-torture.exp: Compute LTO_TORTURE_OPTIONS after the environment * lib/c-torture.exp: Compute LTO_TORTURE_OPTIONS after the environment
......
// { 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);
}
// { 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);
}
// { 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);
}
// 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> ();
// { 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 ();
}
// { 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" } }
// { 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" } }
// { 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)" }
// { 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)" }
...@@ -1929,6 +1929,22 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref) ...@@ -1929,6 +1929,22 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
if (gimple_call_flags (call) if (gimple_call_flags (call)
& (ECF_PURE|ECF_CONST|ECF_LOOPING_CONST_OR_PURE|ECF_NOVOPS)) & (ECF_PURE|ECF_CONST|ECF_LOOPING_CONST_OR_PURE|ECF_NOVOPS))
return false; return false;
if (gimple_call_internal_p (call))
switch (gimple_call_internal_fn (call))
{
/* Treat these internal calls like ECF_PURE for aliasing,
they don't write to any memory the program should care about.
They have important other side-effects, and read memory,
so can't be ECF_NOVOPS. */
case IFN_UBSAN_NULL:
case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
case IFN_UBSAN_OBJECT_SIZE:
case IFN_ASAN_CHECK:
return false;
default:
break;
}
base = ao_ref_base (ref); base = ao_ref_base (ref);
if (!base) if (!base)
......
...@@ -981,6 +981,167 @@ ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi) ...@@ -981,6 +981,167 @@ ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi)
return gsi_end_p (*gsi); return gsi_end_p (*gsi);
} }
/* Cached __ubsan_vptr_type_cache decl. */
static GTY(()) tree ubsan_vptr_type_cache_decl;
/* Expand UBSAN_VPTR internal call. The type is kept on the ckind
argument which is a constant, because the middle-end treats pointer
conversions as useless and therefore the type of the first argument
could be changed to any other pointer type. */
bool
ubsan_expand_vptr_ifn (gimple_stmt_iterator *gsip)
{
gimple_stmt_iterator gsi = *gsip;
gimple stmt = gsi_stmt (gsi);
location_t loc = gimple_location (stmt);
gcc_assert (gimple_call_num_args (stmt) == 5);
tree op = gimple_call_arg (stmt, 0);
tree vptr = gimple_call_arg (stmt, 1);
tree str_hash = gimple_call_arg (stmt, 2);
tree ti_decl_addr = gimple_call_arg (stmt, 3);
tree ckind_tree = gimple_call_arg (stmt, 4);
ubsan_null_ckind ckind = (ubsan_null_ckind) tree_to_uhwi (ckind_tree);
tree type = TREE_TYPE (TREE_TYPE (ckind_tree));
gimple g;
basic_block fallthru_bb = NULL;
if (ckind == UBSAN_DOWNCAST_POINTER)
{
/* Guard everything with if (op != NULL) { ... }. */
basic_block then_bb;
gimple_stmt_iterator cond_insert_point
= create_cond_insert_point (gsip, false, false, true,
&then_bb, &fallthru_bb);
g = gimple_build_cond (NE_EXPR, op, build_zero_cst (TREE_TYPE (op)),
NULL_TREE, NULL_TREE);
gimple_set_location (g, loc);
gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
*gsip = gsi_after_labels (then_bb);
gsi_remove (&gsi, false);
gsi_insert_before (gsip, stmt, GSI_NEW_STMT);
gsi = *gsip;
}
tree htype = TREE_TYPE (str_hash);
tree cst = wide_int_to_tree (htype,
wi::uhwi (((uint64_t) 0x9ddfea08 << 32)
| 0xeb382d69, 64));
g = gimple_build_assign (make_ssa_name (htype), BIT_XOR_EXPR,
vptr, str_hash);
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
g = gimple_build_assign (make_ssa_name (htype), MULT_EXPR,
gimple_assign_lhs (g), cst);
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
tree t1 = gimple_assign_lhs (g);
g = gimple_build_assign (make_ssa_name (htype), LSHIFT_EXPR,
t1, build_int_cst (integer_type_node, 47));
gimple_set_location (g, loc);
tree t2 = gimple_assign_lhs (g);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
g = gimple_build_assign (make_ssa_name (htype), BIT_XOR_EXPR,
vptr, t1);
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
g = gimple_build_assign (make_ssa_name (htype), BIT_XOR_EXPR,
t2, gimple_assign_lhs (g));
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
g = gimple_build_assign (make_ssa_name (htype), MULT_EXPR,
gimple_assign_lhs (g), cst);
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
tree t3 = gimple_assign_lhs (g);
g = gimple_build_assign (make_ssa_name (htype), LSHIFT_EXPR,
t3, build_int_cst (integer_type_node, 47));
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
g = gimple_build_assign (make_ssa_name (htype), BIT_XOR_EXPR,
t3, gimple_assign_lhs (g));
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
g = gimple_build_assign (make_ssa_name (htype), MULT_EXPR,
gimple_assign_lhs (g), cst);
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
if (!useless_type_conversion_p (pointer_sized_int_node, htype))
{
g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
NOP_EXPR, gimple_assign_lhs (g));
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
}
tree hash = gimple_assign_lhs (g);
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;
varpool_node::finalize_decl (array);
ubsan_vptr_type_cache_decl = array;
}
g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
BIT_AND_EXPR, hash,
build_int_cst (pointer_sized_int_node, 127));
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
tree c = build4_loc (loc, ARRAY_REF, pointer_sized_int_node,
ubsan_vptr_type_cache_decl, gimple_assign_lhs (g),
NULL_TREE, NULL_TREE);
g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
ARRAY_REF, c);
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
basic_block then_bb, fallthru2_bb;
gimple_stmt_iterator cond_insert_point
= create_cond_insert_point (gsip, false, false, true,
&then_bb, &fallthru2_bb);
g = gimple_build_cond (NE_EXPR, gimple_assign_lhs (g), hash,
NULL_TREE, NULL_TREE);
gimple_set_location (g, loc);
gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
*gsip = gsi_after_labels (then_bb);
if (fallthru_bb == NULL)
fallthru_bb = fallthru2_bb;
tree data
= ubsan_create_data ("__ubsan_vptr_data", 1, &loc,
ubsan_type_descriptor (type), NULL_TREE, ti_decl_addr,
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;
g = gimple_build_call (builtin_decl_explicit (bcode), 3, data, op, hash);
gimple_set_location (g, loc);
gsi_insert_before (gsip, g, GSI_SAME_STMT);
/* Point GSI to next logical statement. */
*gsip = gsi_start_bb (fallthru_bb);
/* Get rid of the UBSAN_VPTR call from the IR. */
unlink_stmt_vdef (stmt);
gsi_remove (&gsi, true);
return gsi_end_p (*gsip);
}
/* Instrument a memory reference. BASE is the base of MEM, IS_LHS says /* Instrument a memory reference. BASE is the base of MEM, IS_LHS says
whether the pointer is on the left hand side of the assignment. */ whether the pointer is on the left hand side of the assignment. */
......
...@@ -28,7 +28,11 @@ enum ubsan_null_ckind { ...@@ -28,7 +28,11 @@ enum ubsan_null_ckind {
UBSAN_REF_BINDING, UBSAN_REF_BINDING,
UBSAN_MEMBER_ACCESS, UBSAN_MEMBER_ACCESS,
UBSAN_MEMBER_CALL, 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. */ /* This controls how ubsan prints types. Used in ubsan_type_descriptor. */
...@@ -42,6 +46,7 @@ extern bool do_ubsan_in_current_function (void); ...@@ -42,6 +46,7 @@ extern bool do_ubsan_in_current_function (void);
extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *); extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
extern bool ubsan_expand_null_ifn (gimple_stmt_iterator *); extern bool ubsan_expand_null_ifn (gimple_stmt_iterator *);
extern bool ubsan_expand_objsize_ifn (gimple_stmt_iterator *); extern bool ubsan_expand_objsize_ifn (gimple_stmt_iterator *);
extern bool ubsan_expand_vptr_ifn (gimple_stmt_iterator *);
extern bool ubsan_instrument_unreachable (gimple_stmt_iterator *); extern bool ubsan_instrument_unreachable (gimple_stmt_iterator *);
extern tree ubsan_create_data (const char *, int, const location_t *, ...); extern tree ubsan_create_data (const char *, int, const location_t *, ...);
extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL); extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment