diff mbox

Avoid ICEs with throwing calls with -fprintf-return-value (PR middle-end/78901)

Message ID 20170102195403.GK21933@tucnak
State New
Headers show

Commit Message

Jakub Jelinek Jan. 2, 2017, 7:54 p.m. UTC
Hi!

As my earlier patch to adjust cfns.gperf for C99/C11 builtins has been
committed already, the non-__*_chk builtins (and when using __builtin__*_chk
form even those) should be all noexcept, and when using fortification
the glibc headers declare those properly throw() and ssp includes just
use builtins directly.  Therefore I think it is just a waste of time to
add code to handle something that will not really happen in real-world code,
only when somebody declares those incorrectly by hand.

The following patch therefore just punts on stmt_ends_bb_p (info.callstmt)
rather than purging dead edges and scheduling cfg cleanups on replacement
of call with constant if the compiler thinks it might throw, or when
inserting after the call (also invalid, would need to be inserted on
fallthrough edge instead).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2017-01-02  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/78901
	* gimple-ssa-sprintf.c (try_substitute_return_value): Don't change
	possibly throwing calls.

	* g++.dg/opt/pr78901.C: New test.


	Jakub

Comments

Martin Sebor Jan. 2, 2017, 11:38 p.m. UTC | #1
On 01/02/2017 12:54 PM, Jakub Jelinek wrote:
> Hi!
>
> As my earlier patch to adjust cfns.gperf for C99/C11 builtins has been
> committed already, the non-__*_chk builtins (and when using __builtin__*_chk
> form even those) should be all noexcept, and when using fortification
> the glibc headers declare those properly throw() and ssp includes just
> use builtins directly.  Therefore I think it is just a waste of time to
> add code to handle something that will not really happen in real-world code,
> only when somebody declares those incorrectly by hand.
>
> The following patch therefore just punts on stmt_ends_bb_p (info.callstmt)
> rather than purging dead edges and scheduling cfg cleanups on replacement
> of call with constant if the compiler thinks it might throw, or when
> inserting after the call (also invalid, would need to be inserted on
> fallthrough edge instead).

Thanks for taking care of this bug!  Punting on this case sounds
fine.

I was not familiar with the stmt_ends_bb_p() function or how it
relates to exceptions so I looked it up.  It looks to be a wrapper
around is_ctrl_stmt() and is_ctrl_altering_stmt().  Of the two, it
looks to me like the first one will always return false here because
a function call is not a control statement, and the second will end
up returning the result of g gimple_call_ctrl_altering_p() because
the argument is a GIMPLE_CALL.  Would it be appropriate to call
gimple_call_ctrl_altering_p() here directly instead? (I ask mainly
because to me, unlike the former, the latter clearly conveys that
it's being called to determine whether the function call may alter
control flow [such as by throwing].)

Martin

>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>
> 2017-01-02  Jakub Jelinek  <jakub@redhat.com>
>
> 	PR middle-end/78901
> 	* gimple-ssa-sprintf.c (try_substitute_return_value): Don't change
> 	possibly throwing calls.
>
> 	* g++.dg/opt/pr78901.C: New test.
>
> --- gcc/gimple-ssa-sprintf.c.jj	2017-01-02 12:09:29.000000000 +0100
> +++ gcc/gimple-ssa-sprintf.c	2017-01-02 13:05:08.400755201 +0100
> @@ -2699,9 +2699,15 @@ try_substitute_return_value (gimple_stmt
>       the output overflows the destination object (but leave it enabled
>       when the function is bounded because then the behavior is well-
>       defined).  */
> -  if (lhs && res.bounded && res.under4k
> +  if (lhs
> +      && res.bounded
> +      && res.under4k
>        && (info.bounded || res.number_chars <= info.objsize)
> -      && res.number_chars - 1 <= target_int_max ())
> +      && res.number_chars - 1 <= target_int_max ()
> +      /* Not prepared to handle possibly throwing calls here; they shouldn't
> +	 appear in non-artificial testcases, except when the __*_chk routines
> +	 are badly declared.  */
> +      && !stmt_ends_bb_p (info.callstmt))
>      {
>        tree cst = build_int_cst (integer_type_node, res.number_chars - 1);
>
> --- gcc/testsuite/g++.dg/opt/pr78901.C.jj	2017-01-02 13:04:24.271329153 +0100
> +++ gcc/testsuite/g++.dg/opt/pr78901.C	2017-01-02 13:03:19.000000000 +0100
> @@ -0,0 +1,18 @@
> +// PR middle-end/78901
> +// { dg-do compile }
> +// { dg-options "-O2 -Wno-stringop-overflow" }
> +
> +extern "C" int __snprintf_chk (char *, __SIZE_TYPE__, int, __SIZE_TYPE__, const char *, ...);
> +
> +int
> +foo (char *c)
> +{
> +  try
> +    {
> +      return __snprintf_chk (c, 64, 0, 32, "%s", "abcdefghijklmnopq");
> +    }
> +  catch (...)
> +    {
> +      return -1;
> +    }
> +}
>
> 	Jakub
>
Jakub Jelinek Jan. 2, 2017, 11:50 p.m. UTC | #2
On Mon, Jan 02, 2017 at 04:38:03PM -0700, Martin Sebor wrote:
> Thanks for taking care of this bug!  Punting on this case sounds
> fine.
> 
> I was not familiar with the stmt_ends_bb_p() function or how it
> relates to exceptions so I looked it up.  It looks to be a wrapper
> around is_ctrl_stmt() and is_ctrl_altering_stmt().  Of the two, it
> looks to me like the first one will always return false here because
> a function call is not a control statement, and the second will end
> up returning the result of g gimple_call_ctrl_altering_p() because
> the argument is a GIMPLE_CALL.  Would it be appropriate to call
> gimple_call_ctrl_altering_p() here directly instead? (I ask mainly
> because to me, unlike the former, the latter clearly conveys that
> it's being called to determine whether the function call may alter
> control flow [such as by throwing].)

stmt_ends_bb_p is the general predicate for whether stmt has to end a basic
block (and in that case e.g. it is invalid to gsi_insert_after (gsi, g, GSI_NEW_STMT);
when gsi_stmt (*gsi) == stmt), so for the !info.nowrite case it is the right
predicate, because it tests the property we want to avoid.
For the info.nowrite case we care only whether the stmt throws, i.e.
if lookup_stmt_eh_lp (stmt) != 0, but as the stmt_ends_bb_p predicate covers
all those cases and no others, it is IMO fine too.

	Jakub
Richard Biener Jan. 3, 2017, 6:41 a.m. UTC | #3
On January 2, 2017 8:54:03 PM GMT+01:00, Jakub Jelinek <jakub@redhat.com> wrote:
>Hi!
>
>As my earlier patch to adjust cfns.gperf for C99/C11 builtins has been
>committed already, the non-__*_chk builtins (and when using
>__builtin__*_chk
>form even those) should be all noexcept, and when using fortification
>the glibc headers declare those properly throw() and ssp includes just
>use builtins directly.  Therefore I think it is just a waste of time to
>add code to handle something that will not really happen in real-world
>code,
>only when somebody declares those incorrectly by hand.
>
>The following patch therefore just punts on stmt_ends_bb_p
>(info.callstmt)
>rather than purging dead edges and scheduling cfg cleanups on
>replacement
>of call with constant if the compiler thinks it might throw, or when
>inserting after the call (also invalid, would need to be inserted on
>fallthrough edge instead).
>
>Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

Richard.

>2017-01-02  Jakub Jelinek  <jakub@redhat.com>
>
>	PR middle-end/78901
>	* gimple-ssa-sprintf.c (try_substitute_return_value): Don't change
>	possibly throwing calls.
>
>	* g++.dg/opt/pr78901.C: New test.
>
>--- gcc/gimple-ssa-sprintf.c.jj	2017-01-02 12:09:29.000000000 +0100
>+++ gcc/gimple-ssa-sprintf.c	2017-01-02 13:05:08.400755201 +0100
>@@ -2699,9 +2699,15 @@ try_substitute_return_value (gimple_stmt
>      the output overflows the destination object (but leave it enabled
>      when the function is bounded because then the behavior is well-
>      defined).  */
>-  if (lhs && res.bounded && res.under4k
>+  if (lhs
>+      && res.bounded
>+      && res.under4k
>       && (info.bounded || res.number_chars <= info.objsize)
>-      && res.number_chars - 1 <= target_int_max ())
>+      && res.number_chars - 1 <= target_int_max ()
>+      /* Not prepared to handle possibly throwing calls here; they
>shouldn't
>+	 appear in non-artificial testcases, except when the __*_chk routines
>+	 are badly declared.  */
>+      && !stmt_ends_bb_p (info.callstmt))
>     {
>    tree cst = build_int_cst (integer_type_node, res.number_chars - 1);
> 
>--- gcc/testsuite/g++.dg/opt/pr78901.C.jj	2017-01-02 13:04:24.271329153
>+0100
>+++ gcc/testsuite/g++.dg/opt/pr78901.C	2017-01-02 13:03:19.000000000
>+0100
>@@ -0,0 +1,18 @@
>+// PR middle-end/78901
>+// { dg-do compile }
>+// { dg-options "-O2 -Wno-stringop-overflow" }
>+
>+extern "C" int __snprintf_chk (char *, __SIZE_TYPE__, int,
>__SIZE_TYPE__, const char *, ...);
>+
>+int
>+foo (char *c)
>+{
>+  try
>+    {
>+      return __snprintf_chk (c, 64, 0, 32, "%s", "abcdefghijklmnopq");
>+    }
>+  catch (...)
>+    {
>+      return -1;
>+    }
>+}
>
>	Jakub
diff mbox

Patch

--- gcc/gimple-ssa-sprintf.c.jj	2017-01-02 12:09:29.000000000 +0100
+++ gcc/gimple-ssa-sprintf.c	2017-01-02 13:05:08.400755201 +0100
@@ -2699,9 +2699,15 @@  try_substitute_return_value (gimple_stmt
      the output overflows the destination object (but leave it enabled
      when the function is bounded because then the behavior is well-
      defined).  */
-  if (lhs && res.bounded && res.under4k
+  if (lhs
+      && res.bounded
+      && res.under4k
       && (info.bounded || res.number_chars <= info.objsize)
-      && res.number_chars - 1 <= target_int_max ())
+      && res.number_chars - 1 <= target_int_max ()
+      /* Not prepared to handle possibly throwing calls here; they shouldn't
+	 appear in non-artificial testcases, except when the __*_chk routines
+	 are badly declared.  */
+      && !stmt_ends_bb_p (info.callstmt))
     {
       tree cst = build_int_cst (integer_type_node, res.number_chars - 1);
 
--- gcc/testsuite/g++.dg/opt/pr78901.C.jj	2017-01-02 13:04:24.271329153 +0100
+++ gcc/testsuite/g++.dg/opt/pr78901.C	2017-01-02 13:03:19.000000000 +0100
@@ -0,0 +1,18 @@ 
+// PR middle-end/78901
+// { dg-do compile }
+// { dg-options "-O2 -Wno-stringop-overflow" }
+
+extern "C" int __snprintf_chk (char *, __SIZE_TYPE__, int, __SIZE_TYPE__, const char *, ...);
+
+int
+foo (char *c)
+{
+  try
+    {
+      return __snprintf_chk (c, 64, 0, 32, "%s", "abcdefghijklmnopq");
+    }
+  catch (...)
+    {
+      return -1;
+    }
+}