ubsan: default to trap on unreachable at -O0 and -Og [PR104642]
When not optimizing, we can't do anything useful with unreachability in terms of code performance, so we might as well improve debugging by turning __builtin_unreachable into a trap. I think it also makes sense to do this when we're explicitly optimizing for the debugging experience. In the PR richi suggested introducing an -funreachable-traps flag for this. This functionality is already implemented as -fsanitize=unreachable -fsanitize-trap=unreachable, and we want to share the implementation, but it does seem useful to have a separate flag that isn't affected by the various sanitization controls. -fsanitize=unreachable takes priority over -funreachable-traps if both are enabled. Jakub observed that this would slow down -O0 by default from running the sanopt pass, so this revision avoids the need for sanopt by rewriting calls introduced by the compiler immediately, and calls written by the user at fold time. Many of the calls introduced by the compiler are also rewritten immediately to ubsan calls when not trapping, which fixes ubsan-8b.C; previously the call to f() was optimized away before sanopt. But this early rewriting isn't practical for uses of __builtin_unreachable in devirtualization and such, so sanopt rewriting is still done for non-trapping sanitize. PR c++/104642 gcc/ChangeLog: * common.opt: Add -funreachable-traps. * doc/invoke.texi (-funreachable-traps): Document it. * opts.cc (finish_options): Enable at -O0 or -Og. * tree.cc (build_common_builtin_nodes): Add __builtin_trap. (builtin_decl_unreachable, build_builtin_unreachable): New. * tree.h: Declare them. * ubsan.cc (sanitize_unreachable_fn): Factor out. (ubsan_instrument_unreachable): Use gimple_build_builtin_unreachable. * ubsan.h (sanitize_unreachable_fn): Declare. * gimple.cc (gimple_build_builtin_unreachable): New. * gimple.h: Declare it. * builtins.cc (expand_builtin_unreachable): Add assert. (fold_builtin_0): Call build_builtin_unreachable. * sanopt.cc: Don't run for just SANITIZE_RETURN or SANITIZE_UNREACHABLE when trapping. * cgraphunit.cc (walk_polymorphic_call_targets): Use new unreachable functions. * gimple-fold.cc (gimple_fold_call) (gimple_get_virt_method_for_vtable) * ipa-fnsummary.cc (redirect_to_unreachable) * ipa-prop.cc (ipa_make_edge_direct_to_target) (ipa_impossible_devirt_target) * ipa.cc (walk_polymorphic_call_targets) * tree-cfg.cc (pass_warn_function_return::execute) (execute_fixup_cfg) * tree-ssa-loop-ivcanon.cc (remove_exits_and_undefined_stmts) (unloop_loops) * tree-ssa-sccvn.cc (eliminate_dom_walker::eliminate_stmt): Likewise. gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_builtin_function_call): Handle unreachable/trap earlier. * cp-gimplify.cc (cp_maybe_instrument_return): Use build_builtin_unreachable. gcc/testsuite/ChangeLog: * g++.dg/ubsan/return-8a.C: New test. * g++.dg/ubsan/return-8b.C: New test. * g++.dg/ubsan/return-8d.C: New test. * g++.dg/ubsan/return-8e.C: New test.
This commit is contained in:
parent
038b077689
commit
d68d366425
25 changed files with 233 additions and 55 deletions
|
@ -5184,6 +5184,9 @@ expand_builtin_trap (void)
|
|||
static void
|
||||
expand_builtin_unreachable (void)
|
||||
{
|
||||
/* Use gimple_build_builtin_unreachable or builtin_decl_unreachable
|
||||
to avoid this. */
|
||||
gcc_checking_assert (!sanitize_flags_p (SANITIZE_UNREACHABLE));
|
||||
emit_barrier ();
|
||||
}
|
||||
|
||||
|
@ -9261,6 +9264,12 @@ fold_builtin_0 (location_t loc, tree fndecl)
|
|||
case BUILT_IN_CLASSIFY_TYPE:
|
||||
return fold_builtin_classify_type (NULL_TREE);
|
||||
|
||||
case BUILT_IN_UNREACHABLE:
|
||||
/* Rewrite any explicit calls to __builtin_unreachable. */
|
||||
if (sanitize_flags_p (SANITIZE_UNREACHABLE))
|
||||
return build_builtin_unreachable (loc);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1033,8 +1033,7 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
|
|||
if (targets.length () == 1)
|
||||
target = targets[0];
|
||||
else
|
||||
target = cgraph_node::create
|
||||
(builtin_decl_implicit (BUILT_IN_UNREACHABLE));
|
||||
target = cgraph_node::create (builtin_decl_unreachable ());
|
||||
|
||||
if (symtab->dump_file)
|
||||
{
|
||||
|
|
|
@ -3017,6 +3017,10 @@ funit-at-a-time
|
|||
Common Var(flag_unit_at_a_time) Init(1)
|
||||
Compile whole compilation unit at a time.
|
||||
|
||||
funreachable-traps
|
||||
Common Var(flag_unreachable_traps) Optimization
|
||||
Trap on __builtin_unreachable instead of using it for optimization.
|
||||
|
||||
funroll-loops
|
||||
Common Var(flag_unroll_loops) Optimization EnabledBy(funroll-all-loops)
|
||||
Perform loop unrolling when iteration count is known.
|
||||
|
|
|
@ -1438,6 +1438,20 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
|
|||
/* These builtins shall be ignored during constant expression
|
||||
evaluation. */
|
||||
return void_node;
|
||||
case BUILT_IN_UNREACHABLE:
|
||||
case BUILT_IN_TRAP:
|
||||
if (!*non_constant_p && !ctx->quiet)
|
||||
{
|
||||
/* Do not allow__builtin_unreachable in constexpr function.
|
||||
The __builtin_unreachable call with BUILTINS_LOCATION
|
||||
comes from cp_maybe_instrument_return. */
|
||||
if (EXPR_LOCATION (t) == BUILTINS_LOCATION)
|
||||
error ("%<constexpr%> call flows off the end of the function");
|
||||
else
|
||||
error ("%q+E is not a constant expression", t);
|
||||
}
|
||||
*non_constant_p = true;
|
||||
return t;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1531,18 +1545,9 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
|
|||
{
|
||||
if (!*non_constant_p && !ctx->quiet)
|
||||
{
|
||||
/* Do not allow__builtin_unreachable in constexpr function.
|
||||
The __builtin_unreachable call with BUILTINS_LOCATION
|
||||
comes from cp_maybe_instrument_return. */
|
||||
if (fndecl_built_in_p (fun, BUILT_IN_UNREACHABLE)
|
||||
&& EXPR_LOCATION (t) == BUILTINS_LOCATION)
|
||||
error ("%<constexpr%> call flows off the end of the function");
|
||||
else
|
||||
{
|
||||
new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t),
|
||||
CALL_EXPR_FN (t), nargs, args);
|
||||
error ("%q+E is not a constant expression", new_call);
|
||||
}
|
||||
new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t),
|
||||
CALL_EXPR_FN (t), nargs, args);
|
||||
error ("%q+E is not a constant expression", new_call);
|
||||
}
|
||||
*non_constant_p = true;
|
||||
return t;
|
||||
|
|
|
@ -1814,7 +1814,7 @@ cp_maybe_instrument_return (tree fndecl)
|
|||
information is provided, while the __builtin_unreachable () below
|
||||
if return sanitization is disabled will just result in hard to
|
||||
understand runtime error without location. */
|
||||
&& (!optimize
|
||||
&& ((!optimize && !flag_unreachable_traps)
|
||||
|| sanitize_flags_p (SANITIZE_UNREACHABLE, fndecl)))
|
||||
return;
|
||||
|
||||
|
@ -1864,10 +1864,7 @@ cp_maybe_instrument_return (tree fndecl)
|
|||
if (sanitize_flags_p (SANITIZE_RETURN, fndecl))
|
||||
t = ubsan_instrument_return (loc);
|
||||
else
|
||||
{
|
||||
tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
|
||||
t = build_call_expr_loc (BUILTINS_LOCATION, fndecl, 0);
|
||||
}
|
||||
t = build_builtin_unreachable (BUILTINS_LOCATION);
|
||||
|
||||
append_to_statement_list (t, p);
|
||||
}
|
||||
|
|
|
@ -12939,6 +12939,20 @@ also at @option{-O0} if @option{-fsection-anchors} is explicitly requested.
|
|||
Additionally @option{-fno-toplevel-reorder} implies
|
||||
@option{-fno-section-anchors}.
|
||||
|
||||
@item -funreachable-traps
|
||||
@opindex funreachable-traps
|
||||
With this option, the compiler turns calls to
|
||||
@code{__builtin_unreachable} into traps, instead of using them for
|
||||
optimization. This also affects any such calls implicitly generated
|
||||
by the compiler.
|
||||
|
||||
This option has the same effect as @option{-fsanitize=unreachable
|
||||
-fsanitize-trap=unreachable}, but does not affect the values of those
|
||||
options. If @option{-fsanitize=unreachable} is enabled, that option
|
||||
takes priority over this one.
|
||||
|
||||
This option is enabled by default at @option{-O0} and @option{-Og}.
|
||||
|
||||
@item -fweb
|
||||
@opindex fweb
|
||||
Constructs webs as commonly used for register allocation purposes and assign
|
||||
|
|
|
@ -5510,9 +5510,8 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
|
|||
}
|
||||
else
|
||||
{
|
||||
tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
|
||||
gimple *new_stmt = gimple_build_call (fndecl, 0);
|
||||
gimple_set_location (new_stmt, gimple_location (stmt));
|
||||
location_t loc = gimple_location (stmt);
|
||||
gimple *new_stmt = gimple_build_builtin_unreachable (loc);
|
||||
/* If the call had a SSA name as lhs morph that into
|
||||
an uninitialized value. */
|
||||
if (lhs && TREE_CODE (lhs) == SSA_NAME)
|
||||
|
@ -8396,7 +8395,7 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
|
|||
if (!fn
|
||||
|| (TREE_CODE (fn) != ADDR_EXPR && TREE_CODE (fn) != FDESC_EXPR)
|
||||
|| TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
|
||||
fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
|
||||
fn = builtin_decl_unreachable ();
|
||||
else
|
||||
{
|
||||
fn = TREE_OPERAND (fn, 0);
|
||||
|
|
|
@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "asan.h"
|
||||
#include "ubsan.h"
|
||||
#include "langhooks.h"
|
||||
#include "attr-fnspec.h"
|
||||
#include "ipa-modref-tree.h"
|
||||
|
@ -421,6 +422,18 @@ gimple_build_call_from_tree (tree t, tree fnptrtype)
|
|||
return call;
|
||||
}
|
||||
|
||||
/* Build a gcall to __builtin_unreachable as rewritten by
|
||||
-fsanitize=unreachable. */
|
||||
|
||||
gcall *
|
||||
gimple_build_builtin_unreachable (location_t loc)
|
||||
{
|
||||
tree data = NULL_TREE;
|
||||
tree fn = sanitize_unreachable_fn (&data, loc);
|
||||
gcall *g = gimple_build_call (fn, data != NULL_TREE, data);
|
||||
gimple_set_location (g, loc);
|
||||
return g;
|
||||
}
|
||||
|
||||
/* Build a GIMPLE_ASSIGN statement.
|
||||
|
||||
|
|
|
@ -1638,6 +1638,7 @@ extern void maybe_remove_unused_call_args (struct function *, gimple *);
|
|||
extern bool gimple_inexpensive_call_p (gcall *);
|
||||
extern bool stmt_can_terminate_bb_p (gimple *);
|
||||
extern location_t gimple_or_expr_nonartificial_location (gimple *, tree);
|
||||
gcall *gimple_build_builtin_unreachable (location_t);
|
||||
|
||||
/* Return the disposition for a warning (or all warnings by default)
|
||||
for a statement. */
|
||||
|
|
|
@ -250,8 +250,8 @@ static struct cgraph_edge *
|
|||
redirect_to_unreachable (struct cgraph_edge *e)
|
||||
{
|
||||
struct cgraph_node *callee = !e->inline_failed ? e->callee : NULL;
|
||||
struct cgraph_node *target = cgraph_node::get_create
|
||||
(builtin_decl_implicit (BUILT_IN_UNREACHABLE));
|
||||
struct cgraph_node *target
|
||||
= cgraph_node::get_create (builtin_decl_unreachable ());
|
||||
|
||||
if (e->speculative)
|
||||
e = cgraph_edge::resolve_speculation (e, target->decl);
|
||||
|
|
|
@ -3410,7 +3410,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
|
|||
ie->caller->dump_name ());
|
||||
}
|
||||
|
||||
target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
|
||||
target = builtin_decl_unreachable ();
|
||||
callee = cgraph_node::get_create (target);
|
||||
unreachable = true;
|
||||
}
|
||||
|
@ -3821,7 +3821,7 @@ ipa_impossible_devirt_target (struct cgraph_edge *ie, tree target)
|
|||
"No devirtualization target in %s\n",
|
||||
ie->caller->dump_name ());
|
||||
}
|
||||
tree new_target = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
|
||||
tree new_target = builtin_decl_unreachable ();
|
||||
cgraph_node::get_create (new_target);
|
||||
return new_target;
|
||||
}
|
||||
|
|
|
@ -232,8 +232,7 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
|
|||
if (targets.length () == 1)
|
||||
target = targets[0];
|
||||
else
|
||||
target = cgraph_node::get_create
|
||||
(builtin_decl_implicit (BUILT_IN_UNREACHABLE));
|
||||
target = cgraph_node::get_create (builtin_decl_unreachable ());
|
||||
|
||||
if (dump_enabled_p ())
|
||||
{
|
||||
|
|
|
@ -1122,6 +1122,10 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
|
|||
opts->x_flag_no_inline = 1;
|
||||
}
|
||||
|
||||
/* At -O0 or -Og, turn __builtin_unreachable into a trap. */
|
||||
if (!opts->x_optimize || opts->x_optimize_debug)
|
||||
SET_OPTION_IF_UNSET (opts, opts_set, flag_unreachable_traps, true);
|
||||
|
||||
/* Pipelining of outer loops is only possible when general pipelining
|
||||
capabilities are requested. */
|
||||
if (!opts->x_flag_sel_sched_pipelining)
|
||||
|
|
|
@ -942,7 +942,15 @@ public:
|
|||
{}
|
||||
|
||||
/* opt_pass methods: */
|
||||
virtual bool gate (function *) { return flag_sanitize; }
|
||||
virtual bool gate (function *)
|
||||
{
|
||||
/* SANITIZE_RETURN is handled in the front-end. When trapping,
|
||||
SANITIZE_UNREACHABLE is handled by builtin_decl_unreachable. */
|
||||
unsigned int mask = SANITIZE_RETURN;
|
||||
if (flag_sanitize_trap & SANITIZE_UNREACHABLE)
|
||||
mask |= SANITIZE_UNREACHABLE;
|
||||
return flag_sanitize & ~mask;
|
||||
}
|
||||
virtual unsigned int execute (function *);
|
||||
|
||||
}; // class pass_sanopt
|
||||
|
|
16
gcc/testsuite/g++.dg/ubsan/return-8a.C
Normal file
16
gcc/testsuite/g++.dg/ubsan/return-8a.C
Normal file
|
@ -0,0 +1,16 @@
|
|||
// PR c++/104642
|
||||
|
||||
// At -O0 and -Og we default to -funreachable-traps
|
||||
// so the below should abort at runtime.
|
||||
|
||||
// { dg-do run }
|
||||
// { dg-shouldfail { *-*-* } }
|
||||
// { dg-additional-options "-O0" }
|
||||
|
||||
bool b;
|
||||
|
||||
int f() {
|
||||
if (b) return 42;
|
||||
} // { dg-warning "-Wreturn-type" }
|
||||
|
||||
int main() { f(); }
|
17
gcc/testsuite/g++.dg/ubsan/return-8b.C
Normal file
17
gcc/testsuite/g++.dg/ubsan/return-8b.C
Normal file
|
@ -0,0 +1,17 @@
|
|||
// PR c++/104642
|
||||
|
||||
// With -fsanitize=unreachable we shouldn't optimize away the call to f.
|
||||
|
||||
// { dg-do run }
|
||||
// { dg-shouldfail { *-*-* } }
|
||||
// { dg-additional-options "-O -fsanitize=unreachable" }
|
||||
|
||||
bool b;
|
||||
|
||||
int f() {
|
||||
if (b) return 42;
|
||||
__builtin_unreachable ();
|
||||
return 24;
|
||||
}
|
||||
|
||||
int main() { f(); }
|
16
gcc/testsuite/g++.dg/ubsan/return-8d.C
Normal file
16
gcc/testsuite/g++.dg/ubsan/return-8d.C
Normal file
|
@ -0,0 +1,16 @@
|
|||
// PR c++/104642
|
||||
|
||||
// At -O0 and -Og we default to -funreachable-traps
|
||||
// so the below should abort at runtime.
|
||||
|
||||
// { dg-do run }
|
||||
// { dg-shouldfail { *-*-* } }
|
||||
// { dg-additional-options "-Og" }
|
||||
|
||||
bool b;
|
||||
|
||||
int f() {
|
||||
if (b) return 42;
|
||||
} // { dg-warning "-Wreturn-type" }
|
||||
|
||||
int main() { f(); }
|
17
gcc/testsuite/g++.dg/ubsan/return-8e.C
Normal file
17
gcc/testsuite/g++.dg/ubsan/return-8e.C
Normal file
|
@ -0,0 +1,17 @@
|
|||
// PR c++/104642
|
||||
|
||||
// At -O0 and -Og we default to -funreachable-traps
|
||||
// so the below should abort at runtime.
|
||||
|
||||
// { dg-do run }
|
||||
// { dg-shouldfail { *-*-* } }
|
||||
// { dg-additional-options "-O2" }
|
||||
|
||||
bool b;
|
||||
|
||||
__attribute__ ((optimize ("Og")))
|
||||
int f() {
|
||||
if (b) return 42;
|
||||
} // { dg-warning "-Wreturn-type" }
|
||||
|
||||
int main() { f(); }
|
|
@ -9503,9 +9503,8 @@ pass_warn_function_return::execute (function *fun)
|
|||
with __builtin_unreachable () call. */
|
||||
if (optimize && gimple_code (last) == GIMPLE_RETURN)
|
||||
{
|
||||
tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
|
||||
gimple *new_stmt = gimple_build_call (fndecl, 0);
|
||||
gimple_set_location (new_stmt, gimple_location (last));
|
||||
location_t loc = gimple_location (last);
|
||||
gimple *new_stmt = gimple_build_builtin_unreachable (loc);
|
||||
gimple_stmt_iterator gsi = gsi_for_stmt (last);
|
||||
gsi_replace (&gsi, new_stmt, true);
|
||||
remove_edge (e);
|
||||
|
@ -9834,7 +9833,7 @@ execute_fixup_cfg (void)
|
|||
{
|
||||
if (stmt && is_gimple_call (stmt))
|
||||
gimple_call_set_ctrl_altering (stmt, false);
|
||||
tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
|
||||
tree fndecl = builtin_decl_unreachable ();
|
||||
stmt = gimple_build_call (fndecl, 0);
|
||||
gimple_stmt_iterator gsi = gsi_last_bb (bb);
|
||||
gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
|
||||
|
|
|
@ -505,9 +505,8 @@ remove_exits_and_undefined_stmts (class loop *loop, unsigned int npeeled)
|
|||
&& wi::ltu_p (elt->bound, npeeled))
|
||||
{
|
||||
gimple_stmt_iterator gsi = gsi_for_stmt (elt->stmt);
|
||||
gcall *stmt = gimple_build_call
|
||||
(builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0);
|
||||
gimple_set_location (stmt, gimple_location (elt->stmt));
|
||||
location_t loc = gimple_location (elt->stmt);
|
||||
gcall *stmt = gimple_build_builtin_unreachable (loc);
|
||||
gsi_insert_before (&gsi, stmt, GSI_NEW_STMT);
|
||||
split_block (gimple_bb (stmt), stmt);
|
||||
changed = true;
|
||||
|
@ -641,7 +640,7 @@ unloop_loops (bitmap loop_closed_ssa_invalidated,
|
|||
|
||||
/* Create new basic block for the latch edge destination and wire
|
||||
it in. */
|
||||
stmt = gimple_build_call (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0);
|
||||
stmt = gimple_build_builtin_unreachable (locus);
|
||||
latch_edge = make_edge (latch, create_basic_block (NULL, NULL, latch), flags);
|
||||
latch_edge->probability = profile_probability::never ();
|
||||
latch_edge->flags |= flags;
|
||||
|
|
|
@ -6807,7 +6807,7 @@ eliminate_dom_walker::eliminate_stmt (basic_block b, gimple_stmt_iterator *gsi)
|
|||
if (targets.length () == 1)
|
||||
fn = targets[0]->decl;
|
||||
else
|
||||
fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
|
||||
fn = builtin_decl_unreachable ();
|
||||
if (dump_enabled_p ())
|
||||
{
|
||||
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
|
||||
|
|
40
gcc/tree.cc
40
gcc/tree.cc
|
@ -71,6 +71,8 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "gimple-range.h"
|
||||
#include "gomp-constants.h"
|
||||
#include "dfp.h"
|
||||
#include "asan.h"
|
||||
#include "ubsan.h"
|
||||
|
||||
/* Tree code classes. */
|
||||
|
||||
|
@ -9649,6 +9651,7 @@ build_common_builtin_nodes (void)
|
|||
}
|
||||
|
||||
if (!builtin_decl_explicit_p (BUILT_IN_UNREACHABLE)
|
||||
|| !builtin_decl_explicit_p (BUILT_IN_TRAP)
|
||||
|| !builtin_decl_explicit_p (BUILT_IN_ABORT))
|
||||
{
|
||||
ftype = build_function_type (void_type_node, void_list_node);
|
||||
|
@ -9662,6 +9665,10 @@ build_common_builtin_nodes (void)
|
|||
local_define_builtin ("__builtin_abort", ftype, BUILT_IN_ABORT,
|
||||
"abort",
|
||||
ECF_LEAF | ECF_NORETURN | ECF_CONST | ECF_COLD);
|
||||
if (!builtin_decl_explicit_p (BUILT_IN_TRAP))
|
||||
local_define_builtin ("__builtin_trap", ftype, BUILT_IN_TRAP,
|
||||
"__builtin_trap",
|
||||
ECF_NORETURN | ECF_NOTHROW | ECF_LEAF | ECF_COLD);
|
||||
}
|
||||
|
||||
if (!builtin_decl_explicit_p (BUILT_IN_MEMCPY)
|
||||
|
@ -10779,6 +10786,39 @@ build_alloca_call_expr (tree size, unsigned int align, HOST_WIDE_INT max_size)
|
|||
}
|
||||
}
|
||||
|
||||
/* The built-in decl to use to mark code points believed to be unreachable.
|
||||
Typically __builtin_unreachable, but __builtin_trap if
|
||||
-fsanitize=unreachable -fsanitize-trap=unreachable. If only
|
||||
-fsanitize=unreachable, we rely on sanopt to replace calls with the
|
||||
appropriate ubsan function. When building a call directly, use
|
||||
{gimple_},build_builtin_unreachable instead. */
|
||||
|
||||
tree
|
||||
builtin_decl_unreachable ()
|
||||
{
|
||||
enum built_in_function fncode = BUILT_IN_UNREACHABLE;
|
||||
|
||||
if (sanitize_flags_p (SANITIZE_UNREACHABLE)
|
||||
? (flag_sanitize_trap & SANITIZE_UNREACHABLE)
|
||||
: flag_unreachable_traps)
|
||||
fncode = BUILT_IN_TRAP;
|
||||
/* For non-trapping sanitize, we will rewrite __builtin_unreachable () later,
|
||||
in the sanopt pass. */
|
||||
|
||||
return builtin_decl_explicit (fncode);
|
||||
}
|
||||
|
||||
/* Build a call to __builtin_unreachable, possibly rewritten by
|
||||
-fsanitize=unreachable. Use this rather than the above when practical. */
|
||||
|
||||
tree
|
||||
build_builtin_unreachable (location_t loc)
|
||||
{
|
||||
tree data = NULL_TREE;
|
||||
tree fn = sanitize_unreachable_fn (&data, loc);
|
||||
return build_call_expr_loc (loc, fn, data != NULL_TREE, data);
|
||||
}
|
||||
|
||||
/* Create a new constant string literal of type ELTYPE[SIZE] (or LEN
|
||||
if SIZE == -1) and return a tree node representing char* pointer to
|
||||
it as an ADDR_EXPR (ARRAY_REF (ELTYPE, ...)). When STR is nonnull
|
||||
|
|
|
@ -5858,6 +5858,11 @@ builtin_decl_implicit (enum built_in_function fncode)
|
|||
return builtin_info[uns_fncode].decl;
|
||||
}
|
||||
|
||||
/* For BUILTIN_UNREACHABLE, use one of these or
|
||||
gimple_build_builtin_unreachable instead of one of the above. */
|
||||
extern tree builtin_decl_unreachable ();
|
||||
extern tree build_builtin_unreachable (location_t);
|
||||
|
||||
/* Set explicit builtin function nodes and whether it is an implicit
|
||||
function. */
|
||||
|
||||
|
|
48
gcc/ubsan.cc
48
gcc/ubsan.cc
|
@ -638,27 +638,43 @@ ubsan_create_data (const char *name, int loccnt, const location_t *ploc, ...)
|
|||
return var;
|
||||
}
|
||||
|
||||
/* Instrument the __builtin_unreachable call. We just call the libubsan
|
||||
routine instead. */
|
||||
/* Shared between *build_builtin_unreachable. */
|
||||
|
||||
tree
|
||||
sanitize_unreachable_fn (tree *data, location_t loc)
|
||||
{
|
||||
tree fn = NULL_TREE;
|
||||
bool san = sanitize_flags_p (SANITIZE_UNREACHABLE);
|
||||
if (san
|
||||
? (flag_sanitize_trap & SANITIZE_UNREACHABLE)
|
||||
: flag_unreachable_traps)
|
||||
{
|
||||
fn = builtin_decl_explicit (BUILT_IN_TRAP);
|
||||
*data = NULL_TREE;
|
||||
}
|
||||
else if (san)
|
||||
{
|
||||
fn = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE);
|
||||
*data = ubsan_create_data ("__ubsan_unreachable_data", 1, &loc,
|
||||
NULL_TREE, NULL_TREE);
|
||||
*data = build_fold_addr_expr_loc (loc, *data);
|
||||
}
|
||||
else
|
||||
{
|
||||
fn = builtin_decl_explicit (BUILT_IN_UNREACHABLE);
|
||||
*data = NULL_TREE;
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
/* Rewrite a gcall to __builtin_unreachable for -fsanitize=unreachable. Called
|
||||
by the sanopt pass. */
|
||||
|
||||
bool
|
||||
ubsan_instrument_unreachable (gimple_stmt_iterator *gsi)
|
||||
{
|
||||
gimple *g;
|
||||
location_t loc = gimple_location (gsi_stmt (*gsi));
|
||||
|
||||
if (flag_sanitize_trap & SANITIZE_UNREACHABLE)
|
||||
g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
|
||||
else
|
||||
{
|
||||
tree data = ubsan_create_data ("__ubsan_unreachable_data", 1, &loc,
|
||||
NULL_TREE, NULL_TREE);
|
||||
data = build_fold_addr_expr_loc (loc, data);
|
||||
tree fn
|
||||
= builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE);
|
||||
g = gimple_build_call (fn, 1, data);
|
||||
}
|
||||
gimple_set_location (g, loc);
|
||||
gimple *g = gimple_build_builtin_unreachable (loc);
|
||||
gsi_replace (gsi, g, false);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -65,5 +65,6 @@ extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree,
|
|||
tree, tree *);
|
||||
extern tree ubsan_instrument_float_cast (location_t, tree, tree);
|
||||
extern tree ubsan_get_source_location_type (void);
|
||||
extern tree sanitize_unreachable_fn (tree *data, location_t loc);
|
||||
|
||||
#endif /* GCC_UBSAN_H */
|
||||
|
|
Loading…
Add table
Reference in a new issue