On 6/20/22 07:05, Jakub Jelinek wrote:
On Fri, Jun 17, 2022 at 05:20:02PM -0400, Jason Merrill wrote:
Related to PR104642, the current situation where we get less return checking
with just -fsanitize=unreachable than no sanitize flags seems undesirable; I
propose that we do return checking when -fsanitize=unreachable.

__builtin_unreachable itself (unless turned into trap or
__ubsan_handle_builtin_unreachable) is not any kind of return checking, it
is just an optimization.

Yes, but I'm talking about "when -fsanitize=unreachable".

Looks like clang just traps on missing return if not -fsanitize=return, but
the approach in this patch seems more helpful to me if we're already
sanitizing other should-be-unreachable code.

I'm assuming that the difference in treatment of SANITIZE_UNREACHABLE and
SANITIZE_RETURN with regard to loop optimization is deliberate.

return and unreachable are separate sanitizers and such silent one way
implication can have quite unexpected consequences, especially with
-fsanitize-trap=.
Say with -fsanitize=unreachable -fsanitize-trap=unreachable, both current
trunk and clang will link without -lubsan, because the only enabled UBSan
sanitizers use __builtin_trap () which doesn't need library.
With -fsanitize=unreachable silently meaning -fsanitize=unreachable,return
the above would link in -lubsan, because while SANITIZE_UNREACHABLE uses
__builtin_trap, SANITIZE_RETURN doesn't.
Similarly, one has no_sanitize attribute, one could in certain function
__attribute__((no_sanitize ("unreachable"))) and because on the command
line using -fsanitize=unreachable assume other sanitizers aren't enabled,
but the silent addition of return sanitizer would break that.

Ah, true.  How about this approach instead?
From 439c645fa5197ccbfcb6fbbeda5772a8581c3a7e Mon Sep 17 00:00:00 2001
From: Jason Merrill <ja...@redhat.com>
Date: Wed, 15 Jun 2022 15:45:48 -0400
Subject: [PATCH] ubsan: do return check with -fsanitize=unreachable
To: gcc-patches@gcc.gnu.org

The current situation where we get less return checking with just
-fsanitize=unreachable than no sanitize flags seems undesirable; I propose
that we do return checking when -fsanitize=unreachable.

Looks like clang just traps on missing return if not -fsanitize=return, but
the approach in this patch seems more helpful to me if we're already
sanitizing other believed-unreachable code.

I took this approach rather than setting SANITIZE_RETURN in opts.cc so that
attribute no_sanitize ("unreachable") will turn this behavior off as well.

gcc/ChangeLog:

	* doc/invoke.texi: Note that -fsanitize=unreachable includes
	-fsanitize=return.

gcc/c-family/ChangeLog:

	* c-ubsan.cc (ubsan_instrument_return): Also look for trap
	on unreachable.

gcc/cp/ChangeLog:

	* cp-gimplify.cc (cp_maybe_instrument_return): Also instrument
	return if SANITIZE_UNREACHABLE.
---
 gcc/doc/invoke.texi     |  2 ++
 gcc/c-family/c-ubsan.cc |  5 ++++-
 gcc/cp/cp-gimplify.cc   | 14 +-------------
 3 files changed, 7 insertions(+), 14 deletions(-)

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 16a893ec1da..b6786df73b9 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -15950,6 +15950,8 @@ built with this option turned on will issue an error message
 when the end of a non-void function is reached without actually
 returning a value.  This option works in C++ only.
 
+This check is also enabled by -fsanitize=unreachable.
+
 @item -fsanitize=signed-integer-overflow
 @opindex fsanitize=signed-integer-overflow
 This option enables signed integer overflow checking.  We check that
diff --git a/gcc/c-family/c-ubsan.cc b/gcc/c-family/c-ubsan.cc
index 360ba82250c..ac6da558473 100644
--- a/gcc/c-family/c-ubsan.cc
+++ b/gcc/c-family/c-ubsan.cc
@@ -339,7 +339,10 @@ ubsan_instrument_vla (location_t loc, tree size)
 tree
 ubsan_instrument_return (location_t loc)
 {
-  if (flag_sanitize_trap & SANITIZE_RETURN)
+  /* C++ calls this for SANITIZE_RETURN|SANITIZE_UNREACHABLE.  */
+  unsigned mask = flag_sanitize & SANITIZE_RETURN;
+  if (!mask) mask = flag_sanitize & SANITIZE_UNREACHABLE;
+  if (flag_sanitize_trap & mask)
     /* pass_warn_function_return checks for BUILTINS_LOCATION.  */
     return build_call_expr_loc (BUILTINS_LOCATION,
 				builtin_decl_explicit (BUILT_IN_TRAP), 0);
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 7b0465729a3..7214e4be175 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -1806,18 +1806,6 @@ cp_maybe_instrument_return (tree fndecl)
       || !targetm.warn_func_return (fndecl))
     return;
 
-  if (!sanitize_flags_p (SANITIZE_RETURN, fndecl)
-      /* Don't add __builtin_unreachable () if not optimizing, it will not
-	 improve any optimizations in that case, just break UB code.
-	 Don't add it if -fsanitize=unreachable -fno-sanitize=return either,
-	 UBSan covers this with ubsan_instrument_return above where sufficient
-	 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
-	  || sanitize_flags_p (SANITIZE_UNREACHABLE, fndecl)))
-    return;
-
   tree t = DECL_SAVED_TREE (fndecl);
   while (t)
     {
@@ -1861,7 +1849,7 @@ cp_maybe_instrument_return (tree fndecl)
     p = &BIND_EXPR_BODY (*p);
 
   location_t loc = DECL_SOURCE_LOCATION (fndecl);
-  if (sanitize_flags_p (SANITIZE_RETURN, fndecl))
+  if (sanitize_flags_p (SANITIZE_RETURN|SANITIZE_UNREACHABLE, fndecl))
     t = ubsan_instrument_return (loc);
   else
     t = build_builtin_unreachable (BUILTINS_LOCATION);
-- 
2.27.0

Reply via email to