From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2140) id A60893858C3A; Fri, 23 Jun 2023 20:12:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A60893858C3A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1687551166; bh=mNTUHvY9+lSs5rE+ETommiqHh3uR6zn5x6YbCOqs1sE=; h=From:To:Subject:Date:From; b=IiMyRAMSnppstcFB6P7qMfgTrVM5ZnZj1NtnUORLsks7PCc+Lir79WStRFrxK4WK2 Eff5LlNeP7wHB2B4mbXB9vUFyrYhXehWY39gLtQedfRFrSjZ/9/5TvxjFLXMSaVb98 5sgS+vYPfiA1uH+e6WNRYJ4KNNT+y6Iet1+Vci1s= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Alexandre Oliva To: gcc-cvs@gcc.gnu.org Subject: [gcc(refs/users/aoliva/heads/testme)] hardcfr: mark expected-throw functions X-Act-Checkin: gcc X-Git-Author: Alexandre Oliva X-Git-Refname: refs/users/aoliva/heads/testme X-Git-Oldrev: 36904147e6088f1de2634198e6e3ecbcc96a797f X-Git-Newrev: 6e2341b4976d30d33723a43234e3b163bb364c2d Message-Id: <20230623201246.A60893858C3A@sourceware.org> Date: Fri, 23 Jun 2023 20:12:46 +0000 (GMT) List-Id: https://gcc.gnu.org/g:6e2341b4976d30d33723a43234e3b163bb364c2d commit 6e2341b4976d30d33723a43234e3b163bb364c2d Author: Alexandre Oliva Date: Fri Jun 23 17:10:02 2023 -0300 hardcfr: mark expected-throw functions Introduce support for marking functions expected to raise exceptions, and adjust Control Flow Redundancy hardening to add no-xthrow mode for checking noreturn calls, now default, taking these marks into account. The marking is placed as an internal attribute, that will be exposed in a separate patch. for gcc/ChangeLog * tree-core.h (ECF_XTHROW): New macro. * tree.cc (set_call_expr): Add expected_throw attribute when ECF_XTHROW is set. (build_common_builtin_node): Add ECF_XTHROW to __cxa_end_cleanup and _Unwind_Resume or _Unwind_SjLj_Resume. * calls.cc (flags_from_decl_or_type): Check for expected_throw attribute to set ECF_XTHROW. * gimple.cc (gimple_build_call_from_tree): Propagate ECF_XTHROW from decl flags to gimple call... (gimple_call_flags): ... and back. * gimple.h (GF_CALL_XTHROW): New gf_mask flag. (gimple_call_set_expected_throw): New. (gimple_call_expected_throw_p): New. * common.opt (-fhardcfr-check-noreturn-calls=no-xthrow): Add. (no-xthrow): Enable in enum hardcfr_check_noreturn_calls. * doc/invoke.texi: Document it. * flag-types.h (hardcfr_noret): Add HCFRNR_NO_XTHROW, move HCFRNR_UNSPECIFIED next to it to change the default. * gimple-harden-control-flow.cc (always_throwing_noreturn_call_p): Test for noreturn expected_throw calls. for gcc/cp/ChangeLog * decl.cc (push_throw_library_fn): Mark with ECF_XTHROW. * except.cc (build_throw): Likewise __cxa_throw, _ITM_cxa_throw, __cxa_rethrow. for gcc/testsuite/ChangeLog * c-c++-common/torture/harden-cfr-abrt-always.c: New. * c-c++-common/torture/harden-cfr-abrt-never.c: New. * c-c++-common/torture/harden-cfr-abrt-no-xthrow.c: New. * c-c++-common/torture/harden-cfr-abrt-nothrow.c: New. * c-c++-common/torture/harden-cfr-abrt.c: Extend. * c-c++-common/torture/harden-cfr-always.c: New. * c-c++-common/torture/harden-cfr-bret-no-xthrow.c: New. * c-c++-common/torture/harden-cfr-never.c: New. * c-c++-common/torture/harden-cfr-no-xthrow.c: New. * c-c++-common/torture/harden-cfr-nothrow.c: New. * c-c++-common/torture/harden-cfr.c: Prevent full optimization of main. Adjust for new default. * g++.dg/harden-cfr-throw-always-O0.C (NO_OPTIMIZE): Define. * g++.dg/harden-cfr-throw-returning-O0.C (NO_OPTIMIZE): Likewise. * g++.dg/harden-cfr-throw-returning-enabled-O0.C: New. * g++.dg/torture/harden-cfr-throw-always.C [!OPTIMIZE]: Move forced optimizations... * g++.dg/torture/harden-cfr-throw.C: ... here. * g++.dg/torture/harden-cfr-throw-never.C: New. * g++.dg/torture/harden-cfr-throw-no-xthrow.C: New. * g++.dg/torture/harden-cfr-throw-nothrow.C: New. * g++.dg/torture/harden-cfr-throw-nocleanup.C: Adjust flags to retain old defaults. * g++.dg/torture/harden-cfr-throw-returning.C: Likewise. Diff: --- gcc/calls.cc | 3 +++ gcc/common.opt | 12 +++-------- gcc/cp/decl.cc | 3 ++- gcc/cp/except.cc | 8 +++++--- gcc/doc/invoke.texi | 14 ++++++++----- gcc/flag-types.h | 4 ++-- gcc/gimple-harden-control-flow.cc | 19 +++++++++++------- gcc/gimple.cc | 6 ++++++ gcc/gimple.h | 23 ++++++++++++++++++++++ .../c-c++-common/torture/harden-cfr-abrt-always.c | 11 +++++++++++ .../c-c++-common/torture/harden-cfr-abrt-never.c | 11 +++++++++++ .../torture/harden-cfr-abrt-no-xthrow.c | 11 +++++++++++ .../c-c++-common/torture/harden-cfr-abrt-nothrow.c | 11 +++++++++++ .../c-c++-common/torture/harden-cfr-abrt.c | 10 +++++++++- .../c-c++-common/torture/harden-cfr-always.c | 13 ++++++++++++ .../torture/harden-cfr-bret-no-xthrow.c | 14 +++++++++++++ .../c-c++-common/torture/harden-cfr-never.c | 13 ++++++++++++ .../c-c++-common/torture/harden-cfr-no-xthrow.c | 13 ++++++++++++ .../c-c++-common/torture/harden-cfr-nothrow.c | 13 ++++++++++++ gcc/testsuite/c-c++-common/torture/harden-cfr.c | 17 +++++++++------- gcc/testsuite/g++.dg/harden-cfr-throw-always-O0.C | 2 ++ .../g++.dg/harden-cfr-throw-returning-O0.C | 2 ++ .../g++.dg/harden-cfr-throw-returning-enabled-O0.C | 11 +++++++++++ .../g++.dg/torture/harden-cfr-throw-always.C | 7 ------- .../g++.dg/torture/harden-cfr-throw-never.C | 12 +++++++++++ .../g++.dg/torture/harden-cfr-throw-no-xthrow.C | 12 +++++++++++ .../g++.dg/torture/harden-cfr-throw-nocleanup.C | 2 +- .../g++.dg/torture/harden-cfr-throw-nothrow.C | 11 +++++++++++ .../g++.dg/torture/harden-cfr-throw-returning.C | 2 +- gcc/testsuite/g++.dg/torture/harden-cfr-throw.C | 8 ++++++++ gcc/tree-core.h | 3 +++ gcc/tree.cc | 9 +++++++-- 32 files changed, 264 insertions(+), 46 deletions(-) diff --git a/gcc/calls.cc b/gcc/calls.cc index 1f3a6d5c450..0e737441baf 100644 --- a/gcc/calls.cc +++ b/gcc/calls.cc @@ -848,6 +848,9 @@ flags_from_decl_or_type (const_tree exp) flags |= ECF_TM_PURE; } + if (lookup_attribute ("expected_throw", DECL_ATTRIBUTES (exp))) + flags |= ECF_XTHROW; + flags = special_function_p (exp, flags); } else if (TYPE_P (exp)) diff --git a/gcc/common.opt b/gcc/common.opt index aa8379ca0c6..0170aafa597 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1837,7 +1837,7 @@ Check CFR execution paths also when exiting a function through an exception. fhardcfr-check-noreturn-calls= Common Joined RejectNegative Enum(hardcfr_check_noreturn_calls) Var(flag_harden_control_flow_redundancy_check_noreturn) Init(HCFRNR_UNSPECIFIED) Optimization --fhardcfr-check-noreturn-calls=[always|nothrow|never] Check CFR execution paths also before calling noreturn functions. +-fhardcfr-check-noreturn-calls=[always|no-xthrow|nothrow|never] Check CFR execution paths also before calling noreturn functions. Enum Name(hardcfr_check_noreturn_calls) Type(enum hardcfr_noret) UnknownError(unknown hardcfr noreturn checking level %qs) @@ -1848,14 +1848,8 @@ Enum(hardcfr_check_noreturn_calls) String(never) Value(HCFRNR_NEVER) EnumValue Enum(hardcfr_check_noreturn_calls) String(nothrow) Value(HCFRNR_NOTHROW) -; ??? There could be yet another option here, that checked before -; noreturn calls, except for those known to always throw, if we had -; means to distinguish noreturn functions known to always throw, such -; as those used to (re)raise exceptions, from those that merely might -; throw. "not always" stands for "not always-throwing", but it also -; contrasts with "always" below. -; EnumValue -; Enum(hardcfr_check_noreturn_calls) String(not-always) Value(HCFRNR_NOT_ALWAYS) +EnumValue +Enum(hardcfr_check_noreturn_calls) String(no-xthrow) Value(HCFRNR_NO_XTHROW) EnumValue Enum(hardcfr_check_noreturn_calls) String(always) Value(HCFRNR_ALWAYS) diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index c07a4a8d58d..f705f0d16b6 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -5251,7 +5251,8 @@ push_cp_library_fn (enum tree_code operator_code, tree type, tree push_throw_library_fn (tree name, tree type) { - tree fn = push_library_fn (name, type, NULL_TREE, ECF_NORETURN | ECF_COLD); + tree fn = push_library_fn (name, type, NULL_TREE, + ECF_NORETURN | ECF_XTHROW | ECF_COLD); return fn; } diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc index 6c0f0815424..e32efb30457 100644 --- a/gcc/cp/except.cc +++ b/gcc/cp/except.cc @@ -657,12 +657,13 @@ build_throw (location_t loc, tree exp) tree args[3] = {ptr_type_node, ptr_type_node, cleanup_type}; throw_fn = declare_library_fn_1 ("__cxa_throw", - ECF_NORETURN | ECF_COLD, + ECF_NORETURN | ECF_XTHROW | ECF_COLD, void_type_node, 3, args); if (flag_tm && throw_fn != error_mark_node) { tree itm_fn = declare_library_fn_1 ("_ITM_cxa_throw", - ECF_NORETURN | ECF_COLD, + ECF_NORETURN | ECF_XTHROW + | ECF_COLD, void_type_node, 3, args); if (itm_fn != error_mark_node) { @@ -797,7 +798,8 @@ build_throw (location_t loc, tree exp) if (!rethrow_fn) { rethrow_fn = declare_library_fn_1 ("__cxa_rethrow", - ECF_NORETURN | ECF_COLD, + ECF_NORETURN | ECF_XTHROW + | ECF_COLD, void_type_node, 0, NULL); if (flag_tm && rethrow_fn != error_mark_node) apply_tm_attr (rethrow_fn, get_identifier ("transaction_pure")); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index cffff5f3583..17030ca2b8f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -639,7 +639,7 @@ Objective-C and Objective-C++ Dialects}. -fharden-compares -fharden-conditional-branches -fharden-control-flow-redundancy -fhardcfr-check-exceptions -fhardcfr-check-returning-calls --fhardcfr-check-noreturn-calls=@r{[}always@r{|}nothrow@r{|}never@r{]} +-fhardcfr-check-noreturn-calls=@r{[}always@r{|}no-xthrow@r{|}nothrow@r{|}never@r{]} -fstack-protector -fstack-protector-all -fstack-protector-strong -fstack-protector-explicit -fstack-check -fstack-limit-register=@var{reg} -fstack-limit-symbol=@var{sym} @@ -17336,12 +17336,14 @@ enabled (or disabled, using its negated form) explicitly, regardless of the optimizations. @opindex fhardcfr-check-noreturn-calls -@item -fhardcfr-check-noreturn-calls=@r{[}always@r{|}nothrow@r{|}never@r{]} +@item -fhardcfr-check-noreturn-calls=@r{[}always@r{|}no-xthrow@r{|}nothrow@r{|}never@r{]} When @option{-fharden-control-flow-redundancy} is active, check the recorded execution path against the control flow graph before @code{noreturn} calls, either all of them (@option{always}), those that -may not return control to the caller through an exception either -(@option{nothrow}), or none of them (@option{never}, the default). +aren't expected to return control to the caller through an exception +(@option{no-xthrow}, the default), those that may not return control to +the caller through an exception either (@option{nothrow}), or none of +them (@option{never}). Checking before a @code{noreturn} function that may return control to the caller through an exception may cause checking to be performed more @@ -17361,7 +17363,9 @@ attributes, which may affect the placement of checks before calls, as well as the addition of implicit cleanup handlers for them. This unpredictability, and the fact that raising and reraising exceptions frequently amounts to implicitly calling @code{noreturn} functions, have -made @option{never} the default setting for this option. +made @option{no-xthrow} the default setting for this option: it excludes +from the @code{noreturn} treatment only internal functions used to +(re)raise exceptions, that are not affected by these optimizations. @opindex fstack-protector @item -fstack-protector diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 6d6fd19fcd6..2e650bf1c48 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -162,9 +162,9 @@ enum hardcfr_noret { HCFRNR_NEVER, HCFRNR_NOTHROW, - HCFRNR_NOT_ALWAYS, /* Reserved for future use. */ + HCFRNR_NO_XTHROW, + HCFRNR_UNSPECIFIED, HCFRNR_ALWAYS, - HCFRNR_UNSPECIFIED = -1 }; /* The live patching level. */ diff --git a/gcc/gimple-harden-control-flow.cc b/gcc/gimple-harden-control-flow.cc index ed348aea336..38b991879e7 100644 --- a/gcc/gimple-harden-control-flow.cc +++ b/gcc/gimple-harden-control-flow.cc @@ -1075,19 +1075,24 @@ public: } }; -/* It might be useful to avoid checking before noreturn calls that are - known to always finish by throwing an exception, rather than by - ending the program or looping forever. Such functions would have - to be annotated somehow, with an attribute or flag, so that - exception-raising functions, such as C++'s __cxa_throw, +/* Avoid checking before noreturn calls that are known (expected, + really) to finish by throwing an exception, rather than by ending + the program or looping forever. Such functions have to be + annotated, with an attribute (expected_throw) or flag (ECF_XTHROW), + so that exception-raising functions, such as C++'s __cxa_throw, __cxa_rethrow, and Ada's gnat_rcheck_*, gnat_reraise*, ada.exception.raise_exception*, and the language-independent unwinders could be detected here and handled differently from other noreturn functions. */ static bool -always_throwing_noreturn_call_p (gimple *) +always_throwing_noreturn_call_p (gimple *stmt) { - return false; + if (!is_a (stmt)) + return is_a (stmt); + + gcall *call = as_a (stmt); + return (gimple_call_noreturn_p (call) + && gimple_call_expected_throw_p (call)); } /* Control flow redundancy hardening: record the execution path, and diff --git a/gcc/gimple.cc b/gcc/gimple.cc index e0ba42add39..589d01b0249 100644 --- a/gcc/gimple.cc +++ b/gcc/gimple.cc @@ -399,6 +399,10 @@ gimple_build_call_from_tree (tree t, tree fnptrtype) gimple_call_set_from_thunk (call, CALL_FROM_THUNK_P (t)); gimple_call_set_va_arg_pack (call, CALL_EXPR_VA_ARG_PACK (t)); gimple_call_set_nothrow (call, TREE_NOTHROW (t)); + if (fndecl) + gimple_call_set_expected_throw (call, + flags_from_decl_or_type (fndecl) + & ECF_XTHROW); gimple_call_set_by_descriptor (call, CALL_EXPR_BY_DESCRIPTOR (t)); copy_warning (call, t); @@ -1535,6 +1539,8 @@ gimple_call_flags (const gimple *stmt) if (stmt->subcode & GF_CALL_NOTHROW) flags |= ECF_NOTHROW; + if (stmt->subcode & GF_CALL_XTHROW) + flags |= ECF_XTHROW; if (stmt->subcode & GF_CALL_BY_DESCRIPTOR) flags |= ECF_BY_DESCRIPTOR; diff --git a/gcc/gimple.h b/gcc/gimple.h index daf55242f68..3d29009a259 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -150,6 +150,7 @@ enum gf_mask { GF_CALL_BY_DESCRIPTOR = 1 << 10, GF_CALL_NOCF_CHECK = 1 << 11, GF_CALL_FROM_NEW_OR_DELETE = 1 << 12, + GF_CALL_XTHROW = 1 << 13, GF_OMP_PARALLEL_COMBINED = 1 << 0, GF_OMP_TASK_TASKLOOP = 1 << 0, GF_OMP_TASK_TASKWAIT = 1 << 1, @@ -3559,6 +3560,28 @@ gimple_call_nothrow_p (gcall *s) return (gimple_call_flags (s) & ECF_NOTHROW) != 0; } +/* If EXPECTED_THROW_P is true, GIMPLE_CALL S is a call that is known + to be more likely to throw than to run forever, terminate the + program or return by other means. */ + +static inline void +gimple_call_set_expected_throw (gcall *s, bool expected_throw_p) +{ + if (expected_throw_p) + s->subcode |= GF_CALL_XTHROW; + else + s->subcode &= ~GF_CALL_XTHROW; +} + +/* Return true if S is a call that is more likely to end by + propagating an exception than by other means. */ + +static inline bool +gimple_call_expected_throw_p (gcall *s) +{ + return (gimple_call_flags (s) & ECF_XTHROW) != 0; +} + /* If FOR_VAR is true, GIMPLE_CALL S is a call to builtin_alloca that is known to be emitted for VLA objects. Those are wrapped by stack_save/stack_restore calls and hence can't lead to unbounded diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-always.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-always.c new file mode 100644 index 00000000000..26c0f270716 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-always.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=always -fdump-tree-hardcfr -ffat-lto-objects" } */ + +/* Check the noreturn handling of a builtin call with always. */ + +#include "harden-cfr-abrt.c" + +/* Out-of-line checking, before both builtin_abort and return in f. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 2 "hardcfr" } } */ +/* Inline checking before builtin_abort in g. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-never.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-never.c new file mode 100644 index 00000000000..a9eca9893bb --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-never.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=never -fdump-tree-hardcfr -ffat-lto-objects" } */ + +/* Check the noreturn handling of a builtin call with never. */ + +#include "harden-cfr-abrt.c" + +/* No out-of-line checking. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 0 "hardcfr" } } */ +/* Inline checking only before return in f. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-no-xthrow.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-no-xthrow.c new file mode 100644 index 00000000000..eb7589f6d38 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-no-xthrow.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=no-xthrow -fdump-tree-hardcfr -ffat-lto-objects" } */ + +/* Check the noreturn handling of a builtin call with no-xthrow. */ + +#include "harden-cfr-abrt.c" + +/* Out-of-line checking, before both builtin_abort and return in f. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 2 "hardcfr" } } */ +/* Inline checking before builtin_abort in g. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-nothrow.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-nothrow.c new file mode 100644 index 00000000000..24363bdfe57 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt-nothrow.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=nothrow -fdump-tree-hardcfr -ffat-lto-objects" } */ + +/* Check the noreturn handling of a builtin call with =nothrow. */ + +#include "harden-cfr-abrt.c" + +/* Out-of-line checking, before both builtin_abort and return in f. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 2 "hardcfr" } } */ +/* Inline checking before builtin_abort in g. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt.c index 4aada93ad94..1ed727317f1 100644 --- a/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt.c +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-abrt.c @@ -1,11 +1,19 @@ /* { dg-do compile } */ /* { dg-options "-fharden-control-flow-redundancy -fdump-tree-hardcfr -ffat-lto-objects" } */ +/* Check the noreturn handling of a builtin call. */ + int f(int i) { if (!i) __builtin_abort (); return i; } -/* No checking before the noreturn abort, so single inline check. */ +int g() { + __builtin_abort (); +} + +/* Out-of-line checking, before both builtin_abort and return in f. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 2 "hardcfr" } } */ +/* Inline checking before builtin_return in g. */ /* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-always.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-always.c new file mode 100644 index 00000000000..6e0767aad69 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-always.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=always -fdump-tree-hardcfr --param hardcfr-max-blocks=9 --param hardcfr-max-inline-blocks=5 -ffat-lto-objects -w" } */ + +/* Check the instrumentation and the parameters with checking before + all noreturn calls. */ + +#include "harden-cfr.c" + +/* Inlined checking thus trap for f. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ +/* Out-of-line checking for g (param), and before both noreturn calls in main. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 3 "hardcfr" } } */ +/* No checking for h (too many blocks). */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-bret-no-xthrow.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-bret-no-xthrow.c new file mode 100644 index 00000000000..78e5bf41439 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-bret-no-xthrow.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=no-xthrow -fdump-tree-hardcfr -ffat-lto-objects" } */ + +/* Check that, even enabling checks before no-xthrow-throwing noreturn calls + (leaving returning calls enabled), we get checks before __builtin_return + without duplication (__builtin_return is both noreturn and a returning + call). */ + +#include "harden-cfr-bret.c" + +/* Out-of-line checking, before both builtin_return and return in f. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 2 "hardcfr" } } */ +/* Inline checking before builtin_return in g. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-never.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-never.c new file mode 100644 index 00000000000..7fe0bb4a663 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-never.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=never -fdump-tree-hardcfr --param hardcfr-max-blocks=9 --param hardcfr-max-inline-blocks=5 -ffat-lto-objects -w" } */ + +/* Check the instrumentation and the parameters without checking before + noreturn calls. */ + +#include "harden-cfr.c" + +/* Inlined checking thus trap for f. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ +/* Out-of-line checking for g (param). */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 1 "hardcfr" } } */ +/* No checking for h (too many blocks) or main (no edges to exit block). */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-no-xthrow.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-no-xthrow.c new file mode 100644 index 00000000000..56ed9d5d4d5 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-no-xthrow.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=no-xthrow -fdump-tree-hardcfr --param hardcfr-max-blocks=9 --param hardcfr-max-inline-blocks=5 -ffat-lto-objects -w" } */ + +/* Check the instrumentation and the parameters with checking before + all noreturn calls that aren't expected to throw. */ + +#include "harden-cfr.c" + +/* Inlined checking thus trap for f. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ +/* Out-of-line checking for g (param), and before both noreturn calls in main. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 3 "hardcfr" } } */ +/* No checking for h (too many blocks). */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr-nothrow.c b/gcc/testsuite/c-c++-common/torture/harden-cfr-nothrow.c new file mode 100644 index 00000000000..da54fc0b57a --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr-nothrow.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=nothrow -fdump-tree-hardcfr --param hardcfr-max-blocks=9 --param hardcfr-max-inline-blocks=5 -ffat-lto-objects -w" } */ + +/* Check the instrumentation and the parameters without checking before + nothrow noreturn calls. */ + +#include "harden-cfr.c" + +/* Inlined checking thus trap for f. */ +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ +/* Out-of-line checking for g (param), and before both noreturn calls in main. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 3 "hardcfr" } } */ +/* No checking for h (too many blocks). */ diff --git a/gcc/testsuite/c-c++-common/torture/harden-cfr.c b/gcc/testsuite/c-c++-common/torture/harden-cfr.c index 2f5946c0387..73824c66f50 100644 --- a/gcc/testsuite/c-c++-common/torture/harden-cfr.c +++ b/gcc/testsuite/c-c++-common/torture/harden-cfr.c @@ -1,6 +1,8 @@ /* { dg-do run } */ /* { dg-options "-fharden-control-flow-redundancy -fdump-tree-hardcfr --param hardcfr-max-blocks=9 --param hardcfr-max-inline-blocks=5 -ffat-lto-objects" } */ +/* Check the instrumentation and the parameters. */ + int f (int i, int j) { @@ -64,18 +66,19 @@ h (unsigned i, int j) /* { dg-warning "has more than 9 blocks, the requested max } int -main () +main (int argc, char *argv[]) { - if (f (1, 2) != 2 || f (3, 2) != 6 - || g (2, 5) != 25 || h (4, 3) != 33) + if (f (1, 2) != 2 || g (2, 5) != 25 || h (4, 3) != 33 + || argc < 0) __builtin_abort (); /* Call exit, instead of returning, to avoid an edge to the exit block and - thus implicitly disable hardening of main. */ + thus implicitly disable hardening of main, when checking before noreturn + calls is disabled. */ __builtin_exit (0); } /* Inlined checking thus trap for f. */ /* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "hardcfr" } } */ -/* Out-of-line checking for g. */ -/* { dg-final { scan-tree-dump-times "__hardcfr_check" 1 "hardcfr" } } */ -/* No checking for h (too many blocks) or main (no edges to exit block). */ +/* Out-of-line checking for g (param), and before both noreturn calls in main. */ +/* { dg-final { scan-tree-dump-times "__hardcfr_check" 3 "hardcfr" } } */ +/* No checking for h (too many blocks). */ diff --git a/gcc/testsuite/g++.dg/harden-cfr-throw-always-O0.C b/gcc/testsuite/g++.dg/harden-cfr-throw-always-O0.C index 17ea79f7cfb..e3c109b89c5 100644 --- a/gcc/testsuite/g++.dg/harden-cfr-throw-always-O0.C +++ b/gcc/testsuite/g++.dg/harden-cfr-throw-always-O0.C @@ -5,6 +5,8 @@ maybe-throwing functions, and also checking before noreturn calls. h2 and h2b get an extra resx without ehcleanup. */ +#define NO_OPTIMIZE + #include "torture/harden-cfr-throw.C" /* { dg-final { scan-tree-dump-times "hardcfr_check" 16 "hardcfr" } } */ diff --git a/gcc/testsuite/g++.dg/harden-cfr-throw-returning-O0.C b/gcc/testsuite/g++.dg/harden-cfr-throw-returning-O0.C index f0338ccc361..207bdb7471a 100644 --- a/gcc/testsuite/g++.dg/harden-cfr-throw-returning-O0.C +++ b/gcc/testsuite/g++.dg/harden-cfr-throw-returning-O0.C @@ -4,6 +4,8 @@ /* -fhardcfr-check-returning-calls gets implicitly disabled because, -at O0, -foptimize-sibling-calls has no effect. */ +#define NO_OPTIMIZE + #include "torture/harden-cfr-throw.C" /* { dg-final { scan-tree-dump-times "hardcfr_check" 12 "hardcfr" } } */ diff --git a/gcc/testsuite/g++.dg/harden-cfr-throw-returning-enabled-O0.C b/gcc/testsuite/g++.dg/harden-cfr-throw-returning-enabled-O0.C new file mode 100644 index 00000000000..b2df689c932 --- /dev/null +++ b/gcc/testsuite/g++.dg/harden-cfr-throw-returning-enabled-O0.C @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-returning-calls -fdump-tree-hardcfr -O0" } */ + +/* Explicitly enable -fhardcfr-check-returning-calls -at O0. */ + +#include "torture/harden-cfr-throw.C" + +/* Same expectations as those in torture/harden-cfr-throw-returning.C. */ + +/* { dg-final { scan-tree-dump-times "hardcfr_check" 10 "hardcfr" } } */ +/* { dg-final { scan-tree-dump-times "builtin_trap" 2 "hardcfr" } } */ diff --git a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-always.C b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-always.C index 0286f6e6d3f..4d303e769ef 100644 --- a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-always.C +++ b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-always.C @@ -5,13 +5,6 @@ maybe-throwing functions, and also checking before noreturn calls. */ -#if ! __OPTIMIZE__ -/* Without optimization, functions with cleanups end up with an extra - resx that is not optimized out, so arrange to optimize them. */ -void __attribute__ ((__optimize__ (1))) h2(void); -void __attribute__ ((__optimize__ (1))) h2b(void); -#endif - #include "harden-cfr-throw.C" /* { dg-final { scan-tree-dump-times "hardcfr_check" 14 "hardcfr" } } */ diff --git a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-never.C b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-never.C new file mode 100644 index 00000000000..81c1b1abae6 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-never.C @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fno-hardcfr-check-returning-calls -fhardcfr-check-noreturn-calls=never -fdump-tree-hardcfr -ffat-lto-objects" } */ + +/* Check that we insert cleanups for checking around the bodies of + maybe-throwing functions, without checking before noreturn + calls. */ + +#include "harden-cfr-throw.C" + +/* { dg-final { scan-tree-dump-times "hardcfr_check" 12 "hardcfr" } } */ +/* { dg-final { scan-tree-dump-times "builtin_trap" 1 "hardcfr" } } */ +/* { dg-final { scan-tree-dump-times "Bypassing" 0 "hardcfr" } } */ diff --git a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-no-xthrow.C b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-no-xthrow.C new file mode 100644 index 00000000000..720498b4bbc --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-no-xthrow.C @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fno-hardcfr-check-returning-calls -fhardcfr-check-noreturn-calls=no-xthrow -fdump-tree-hardcfr -ffat-lto-objects" } */ + +/* Check that we insert cleanups for checking around the bodies of + maybe-throwing functions, and also checking before noreturn + calls. */ + +#include "harden-cfr-throw.C" + +/* { dg-final { scan-tree-dump-times "hardcfr_check" 12 "hardcfr" } } */ +/* { dg-final { scan-tree-dump-times "builtin_trap" 1 "hardcfr" } } */ +/* { dg-final { scan-tree-dump-times "Bypassing" 0 "hardcfr" } } */ diff --git a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-nocleanup.C b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-nocleanup.C index 885b0b236af..9f359363d17 100644 --- a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-nocleanup.C +++ b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-nocleanup.C @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fharden-control-flow-redundancy -fno-hardcfr-check-exceptions -fno-hardcfr-check-returning-calls -fdump-tree-hardcfr -ffat-lto-objects" } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=never -fno-hardcfr-check-exceptions -fno-hardcfr-check-returning-calls -fdump-tree-hardcfr -ffat-lto-objects" } */ /* Check that we do not insert cleanups for checking around the bodies of maybe-throwing functions. h4 doesn't get any checks, because we diff --git a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-nothrow.C b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-nothrow.C new file mode 100644 index 00000000000..e1c2e8d73bb --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-nothrow.C @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fharden-control-flow-redundancy -fno-hardcfr-check-returning-calls -fhardcfr-check-noreturn-calls=nothrow -fdump-tree-hardcfr -ffat-lto-objects" } */ + +/* Check that we insert cleanups for checking around the bodies of + maybe-throwing functions, without checking before noreturn + calls. */ + +#include "harden-cfr-throw.C" + +/* { dg-final { scan-tree-dump-times "hardcfr_check" 12 "hardcfr" } } */ +/* { dg-final { scan-tree-dump-times "builtin_trap" 1 "hardcfr" } } */ diff --git a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-returning.C b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-returning.C index 32def637255..37e4551d096 100644 --- a/gcc/testsuite/g++.dg/torture/harden-cfr-throw-returning.C +++ b/gcc/testsuite/g++.dg/torture/harden-cfr-throw-returning.C @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fharden-control-flow-redundancy -foptimize-sibling-calls -fdump-tree-hardcfr -ffat-lto-objects" } */ +/* { dg-options "-fharden-control-flow-redundancy -fhardcfr-check-noreturn-calls=never -foptimize-sibling-calls -fdump-tree-hardcfr -ffat-lto-objects" } */ /* Check that we insert cleanups for checking around the bodies of maybe-throwing functions. These results depend on checking before diff --git a/gcc/testsuite/g++.dg/torture/harden-cfr-throw.C b/gcc/testsuite/g++.dg/torture/harden-cfr-throw.C index 992fbdad381..8e46b900cd2 100644 --- a/gcc/testsuite/g++.dg/torture/harden-cfr-throw.C +++ b/gcc/testsuite/g++.dg/torture/harden-cfr-throw.C @@ -1,6 +1,13 @@ /* { dg-do compile } */ /* { dg-options "-fharden-control-flow-redundancy -fno-hardcfr-check-returning-calls -fdump-tree-hardcfr -ffat-lto-objects" } */ +#if ! __OPTIMIZE__ && ! defined NO_OPTIMIZE +/* Without optimization, functions with cleanups end up with an extra + resx that is not optimized out, so arrange to optimize them. */ +void __attribute__ ((__optimize__ (1))) h2(void); +void __attribute__ ((__optimize__ (1))) h2b(void); +#endif + /* Check that we insert cleanups for checking around the bodies of maybe-throwing functions. */ @@ -63,3 +70,4 @@ void h4(void) { /* { dg-final { scan-tree-dump-times "hardcfr_check" 12 "hardcfr" } } */ /* { dg-final { scan-tree-dump-times "builtin_trap" 1 "hardcfr" } } */ +/* { dg-final { scan-tree-dump-times "Bypassing" 0 "hardcfr" } } */ diff --git a/gcc/tree-core.h b/gcc/tree-core.h index c48a12b378f..b30305db324 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -95,6 +95,9 @@ struct die_struct; /* Nonzero if this is a cold function. */ #define ECF_COLD (1 << 15) +/* Nonzero if this is a function expected to end with an exception. */ +#define ECF_XTHROW (1 << 16) + /* Call argument flags. */ /* Nonzero if the argument is not used by the function. */ diff --git a/gcc/tree.cc b/gcc/tree.cc index 8e144bc090e..e56a3e47267 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -9700,6 +9700,10 @@ set_call_expr_flags (tree decl, int flags) DECL_ATTRIBUTES (decl)); if ((flags & ECF_TM_PURE) && flag_tm) apply_tm_attr (decl, get_identifier ("transaction_pure")); + if ((flags & ECF_XTHROW)) + DECL_ATTRIBUTES (decl) + = tree_cons (get_identifier ("expected_throw"), + NULL, DECL_ATTRIBUTES (decl)); /* Looping const or pure is implied by noreturn. There is currently no way to declare looping const or looping pure alone. */ gcc_assert (!(flags & ECF_LOOPING_CONST_OR_PURE) @@ -9912,7 +9916,8 @@ build_common_builtin_nodes (void) ftype = build_function_type_list (void_type_node, NULL_TREE); local_define_builtin ("__builtin_cxa_end_cleanup", ftype, BUILT_IN_CXA_END_CLEANUP, - "__cxa_end_cleanup", ECF_NORETURN | ECF_LEAF); + "__cxa_end_cleanup", + ECF_NORETURN | ECF_XTHROW | ECF_LEAF); } ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); @@ -9921,7 +9926,7 @@ build_common_builtin_nodes (void) ((targetm_common.except_unwind_info (&global_options) == UI_SJLJ) ? "_Unwind_SjLj_Resume" : "_Unwind_Resume"), - ECF_NORETURN); + ECF_NORETURN | ECF_XTHROW); if (builtin_decl_explicit (BUILT_IN_RETURN_ADDRESS) == NULL_TREE) {