Re: [patch, libgfortran] Part 2: PR105456 Child I/O does not propage iostat

2024-03-05 Thread Steve Kargl
On Tue, Mar 05, 2024 at 08:06:10PM -0800, Jerry D wrote:
> On 3/5/24 1:51 PM, Harald Anlauf wrote:
> > Hi Jerry,
> > 
> > on further thought, do we sanitize 'child_iomsg'?
> > We pass it to snprintf as format.
> > 
> > Wouldn't a strncpy be sufficient?
> > 
> > Harald
> > 
> > 
> 
> Just to be safe I will bump char message[IOMSG_LEN] to char
> message[IOMSG_LEN + 1]
> 
> This is like a C string vs a Fortran string length situation. snprintf
> guarantees we don't exceed the child_iomsg_len and null terminates it.
> 
> I added 1 to:
>  child_iomsg_len = string_len_trim (IOMSG_LEN, child_iomsg) + 1
> 

string_len_trim substracts 1 from the passed in argument.

gfc_charlen_type
string_len_trim (gfc_charlen_type len, const CHARTYPE *s)
{
  if (len <= 0)
return 0;

  const size_t long_len = sizeof (unsigned long);

  size_t i = len - 1;


Does this account for the NULL?

-- 
steve


Re: [patch, libgfortran] Part 2: PR105456 Child I/O does not propage iostat

2024-03-05 Thread Jerry D

On 3/5/24 1:51 PM, Harald Anlauf wrote:

Hi Jerry,

on further thought, do we sanitize 'child_iomsg'?
We pass it to snprintf as format.

Wouldn't a strncpy be sufficient?

Harald




Just to be safe I will bump char message[IOMSG_LEN] to char 
message[IOMSG_LEN + 1]


This is like a C string vs a Fortran string length situation. snprintf 
guarantees we don't exceed the child_iomsg_len and null terminates it.


I added 1 to:
 child_iomsg_len = string_len_trim (IOMSG_LEN, child_iomsg) + 1

Because snprintf was chopping off the last character of the fortran 
string to put the null in. (zero based vs one based char array). I test 
this with a very long string which exceeded the length and then reduced 
it until I could see the desired end.


I have not tried running a test case with sanitize. I did check with 
valgrind.  I will try the sanitize flags to see if we get a problem.  If 
not will push.


Thanks for comments,

Jerry -


On 3/5/24 22:37, Harald Anlauf wrote:

Hi Jerry,

I think there is the risk of buffer overrun in the following places:

+ char message[IOMSG_LEN];
+ child_iomsg_len = string_len_trim (IOMSG_LEN, child_iomsg)
+ 1;
   free_line (dtp);
   snprintf (message, child_iomsg_len, child_iomsg);
   generate_error (>common, dtp->u.p.child_saved_iostat,

plus several more.  Wouldn't it be better to increase the size of
message by one?

Thanks,
Harald


On 3/5/24 04:15, Jerry D wrote:

On 3/1/24 11:24 AM, rep.dot@gmail.com wrote:

Hi Jerry and Steve,

On 29 February 2024 19:28:19 CET, Jerry D  wrote:

On 2/29/24 10:13 AM, Steve Kargl wrote:

On Thu, Feb 29, 2024 at 09:36:43AM -0800, Jerry D wrote:

On 2/29/24 1:47 AM, Bernhard Reutner-Fischer wrote:


And, just for my own education, the length limitation of iomsg to
255
chars is not backed by the standard AFAICS, right? It's just our
STRERR_MAXSZ?


Yes, its what we have had for a long lone time. Once you throw an
error
things get very processor dependent. I found MSGLEN set to 100 and
IOMSG_len
to 256. Nothing magic about it.



There is no restriction on the length for the iomsg-variable
that receives the generated error message.  In fact, if the
iomsg-variable has a deferred-length type parameter, then
(re)-allocation to the exact length is expected.

    F2023

    12.11.6 IOMSG= specifier

    If an error, end-of-file, or end-of-record condition occurs 
during
    execution of an input/output statement, iomsg-variable is 
assigned

    an explanatory message, as if by intrinsic assignment. If no such
    condition occurs, the definition status and value of
iomsg-variable
    are unchanged.
   character(len=23) emsg
read(fd,*,iomsg=emsg)

Here, the generated iomsg is either truncated to a length of 23
or padded with blanks to a length of 23.

character(len=:), allocatable :: emsg
read(fd,*,iomsg=emsg)

Here, emsg should have the length of whatever error message was
generated.
   HTH



Well, currently, if someone uses a larger string than 256 we are
going to chop it off.

Do we want to process this differently now?


Yes. There is some odd hunk about discrepancy of passed len and
actual len afterwards in 22-007-r1, IIRC. Didn't look closely though.


--- snip ---

Attached is the revised patch using the already available
string_len_trim function.

This hunk is only executed if a user has not passed an iostat or iomsg
variable in the parent I/O statement and an error is triggered which
terminates execution of the program. In this case, the iomsg string is
provided in the usual error message in a "processor defined" way.

(F2023):

12.6.4.8.3 Executing defined input/output data transfers
---
11 If the iostat argument of the defined input/output procedure has a
nonzero value when that procedure returns, and the processor therefore
terminates execution of the program as described in 12.11, the
processor shall make the value of the iomsg argument available in a
processor-dependent manner.
---

OK for trunk?

Regards,

Jerry












Re: [patch, libgfortran] Part 2: PR105456 Child I/O does not propage iostat

2024-03-05 Thread Harald Anlauf

Hi Jerry,

on further thought, do we sanitize 'child_iomsg'?
We pass it to snprintf as format.

Wouldn't a strncpy be sufficient?

Harald


On 3/5/24 22:37, Harald Anlauf wrote:

Hi Jerry,

I think there is the risk of buffer overrun in the following places:

+ char message[IOMSG_LEN];
+ child_iomsg_len = string_len_trim (IOMSG_LEN, child_iomsg)
+ 1;
   free_line (dtp);
   snprintf (message, child_iomsg_len, child_iomsg);
   generate_error (>common, dtp->u.p.child_saved_iostat,

plus several more.  Wouldn't it be better to increase the size of
message by one?

Thanks,
Harald


On 3/5/24 04:15, Jerry D wrote:

On 3/1/24 11:24 AM, rep.dot@gmail.com wrote:

Hi Jerry and Steve,

On 29 February 2024 19:28:19 CET, Jerry D  wrote:

On 2/29/24 10:13 AM, Steve Kargl wrote:

On Thu, Feb 29, 2024 at 09:36:43AM -0800, Jerry D wrote:

On 2/29/24 1:47 AM, Bernhard Reutner-Fischer wrote:


And, just for my own education, the length limitation of iomsg to
255
chars is not backed by the standard AFAICS, right? It's just our
STRERR_MAXSZ?


Yes, its what we have had for a long lone time. Once you throw an
error
things get very processor dependent. I found MSGLEN set to 100 and
IOMSG_len
to 256. Nothing magic about it.



There is no restriction on the length for the iomsg-variable
that receives the generated error message.  In fact, if the
iomsg-variable has a deferred-length type parameter, then
(re)-allocation to the exact length is expected.

    F2023

    12.11.6 IOMSG= specifier

    If an error, end-of-file, or end-of-record condition occurs during
    execution of an input/output statement, iomsg-variable is assigned
    an explanatory message, as if by intrinsic assignment. If no such
    condition occurs, the definition status and value of
iomsg-variable
    are unchanged.
   character(len=23) emsg
read(fd,*,iomsg=emsg)

Here, the generated iomsg is either truncated to a length of 23
or padded with blanks to a length of 23.

character(len=:), allocatable :: emsg
read(fd,*,iomsg=emsg)

Here, emsg should have the length of whatever error message was
generated.
   HTH



Well, currently, if someone uses a larger string than 256 we are
going to chop it off.

Do we want to process this differently now?


Yes. There is some odd hunk about discrepancy of passed len and
actual len afterwards in 22-007-r1, IIRC. Didn't look closely though.


--- snip ---

Attached is the revised patch using the already available
string_len_trim function.

This hunk is only executed if a user has not passed an iostat or iomsg
variable in the parent I/O statement and an error is triggered which
terminates execution of the program. In this case, the iomsg string is
provided in the usual error message in a "processor defined" way.

(F2023):

12.6.4.8.3 Executing defined input/output data transfers
---
11 If the iostat argument of the defined input/output procedure has a
nonzero value when that procedure returns, and the processor therefore
terminates execution of the program as described in 12.11, the
processor shall make the value of the iomsg argument available in a
processor-dependent manner.
---

OK for trunk?

Regards,

Jerry










Re: [patch, libgfortran] Part 2: PR105456 Child I/O does not propage iostat

2024-03-05 Thread Harald Anlauf

Hi Jerry,

I think there is the risk of buffer overrun in the following places:

+ char message[IOMSG_LEN];
+ child_iomsg_len = string_len_trim (IOMSG_LEN, child_iomsg)
+ 1;
  free_line (dtp);
  snprintf (message, child_iomsg_len, child_iomsg);
  generate_error (>common, dtp->u.p.child_saved_iostat,

plus several more.  Wouldn't it be better to increase the size of
message by one?

Thanks,
Harald


On 3/5/24 04:15, Jerry D wrote:

On 3/1/24 11:24 AM, rep.dot@gmail.com wrote:

Hi Jerry and Steve,

On 29 February 2024 19:28:19 CET, Jerry D  wrote:

On 2/29/24 10:13 AM, Steve Kargl wrote:

On Thu, Feb 29, 2024 at 09:36:43AM -0800, Jerry D wrote:

On 2/29/24 1:47 AM, Bernhard Reutner-Fischer wrote:


And, just for my own education, the length limitation of iomsg to 255
chars is not backed by the standard AFAICS, right? It's just our
STRERR_MAXSZ?


Yes, its what we have had for a long lone time. Once you throw an
error
things get very processor dependent. I found MSGLEN set to 100 and
IOMSG_len
to 256. Nothing magic about it.



There is no restriction on the length for the iomsg-variable
that receives the generated error message.  In fact, if the
iomsg-variable has a deferred-length type parameter, then
(re)-allocation to the exact length is expected.

    F2023

    12.11.6 IOMSG= specifier

    If an error, end-of-file, or end-of-record condition occurs during
    execution of an input/output statement, iomsg-variable is assigned
    an explanatory message, as if by intrinsic assignment. If no such
    condition occurs, the definition status and value of iomsg-variable
    are unchanged.
   character(len=23) emsg
read(fd,*,iomsg=emsg)

Here, the generated iomsg is either truncated to a length of 23
or padded with blanks to a length of 23.

character(len=:), allocatable :: emsg
read(fd,*,iomsg=emsg)

Here, emsg should have the length of whatever error message was
generated.
   HTH



Well, currently, if someone uses a larger string than 256 we are
going to chop it off.

Do we want to process this differently now?


Yes. There is some odd hunk about discrepancy of passed len and actual
len afterwards in 22-007-r1, IIRC. Didn't look closely though.


--- snip ---

Attached is the revised patch using the already available
string_len_trim function.

This hunk is only executed if a user has not passed an iostat or iomsg
variable in the parent I/O statement and an error is triggered which
terminates execution of the program. In this case, the iomsg string is
provided in the usual error message in a "processor defined" way.

(F2023):

12.6.4.8.3 Executing defined input/output data transfers
---
11 If the iostat argument of the defined input/output procedure has a
nonzero value when that procedure returns, and the processor therefore
terminates execution of the program as described in 12.11, the processor
shall make the value of the iomsg argument available in a
processor-dependent manner.
---

OK for trunk?

Regards,

Jerry






Re: [patch, libgfortran] Part 2: PR105456 Child I/O does not propage iostat

2024-03-05 Thread rep . dot . nop
On 5 March 2024 04:15:12 CET, Jerry D  wrote:

>
>Attached is the revised patch using the already available string_len_trim 
>function.
>
>This hunk is only executed if a user has not passed an iostat or iomsg 
>variable in the parent I/O statement and an error is triggered which 
>terminates execution of the program. In this case, the iomsg string is 
>provided in the usual error message in a "processor defined" way.
>
>(F2023):
>
>12.6.4.8.3 Executing defined input/output data transfers
>---
>11 If the iostat argument of the defined input/output procedure has a nonzero 
>value when that procedure returns, and the processor therefore terminates 
>execution of the program as described in 12.11, the processor shall make the 
>value of the iomsg argument available in a processor-dependent manner.
>---
>
>OK for trunk?

LGTM.
thanks!


[PATCH] Fortran: error recovery while simplifying expressions [PR103707,PR106987]

2024-03-05 Thread Harald Anlauf
Dear all,

error recovery on arithmetic errors during simplification has bugged
me for a long time, especially since the occurence of ICEs depended
on whether -frange-check is specified or not, whether array ctors
were involved, etc.

I've now come up with the attached patch that classifies the arithmetic
result codes into "hard" and "soft" errors.

A "soft" error means that it is an overflow or other exception (e.g. NaN)
that is ignored with -fno-range-check.  After the patch, a soft error
will not stop simplification (a hard one will), and error status will be
passed along.

I took this opportunity to change the emitted error for division by zero
for real and complex division dependent on whether the numerator is
regular or not.  This makes e.g. (0.)/0 a NaN and now says so, in
accordance with some other brands.

Regtested on x86_64-pc-linux-gnu.  OK for mainline?

Other comments?

Thanks,
Harald

From d9b87bea6af77fbc794e1f21cfecb0468c68cb72 Mon Sep 17 00:00:00 2001
From: Harald Anlauf 
Date: Tue, 5 Mar 2024 21:54:26 +0100
Subject: [PATCH] Fortran: error recovery while simplifying expressions
 [PR103707,PR106987]

When an exception is encountered during simplification of arithmetic
expressions, the result may depend on whether range-checking is active
(-frange-check) or not.  However, the code path in the front-end should
stay the same for "soft" errors for which the exception is triggered by the
check, while "hard" errors should always terminate the simplification, so
that error recovery is independent of the flag.  Separation of arithmetic
error codes into "hard" and "soft" errors shall be done consistently via
is_hard_arith_error().

	PR fortran/103707
	PR fortran/106987

gcc/fortran/ChangeLog:

	* arith.cc (is_hard_arith_error): New helper function to determine
	whether an arithmetic error is "hard" or not.
	(check_result): Use it.
	(gfc_arith_divide): Set "Division by zero" only for regular
	numerators of real and complex divisions.
	(reduce_unary): Use is_hard_arith_error to determine whether a hard
	or (recoverable) soft error was encountered.  Terminate immediately
	on hard error, otherwise remember code of first soft error.
	(reduce_binary_ac): Likewise.
	(reduce_binary_ca): Likewise.
	(reduce_binary_aa): Likewise.

gcc/testsuite/ChangeLog:

	* gfortran.dg/pr99350.f90:
	* gfortran.dg/arithmetic_overflow_3.f90: New test.
---
 gcc/fortran/arith.cc  | 134 --
 .../gfortran.dg/arithmetic_overflow_3.f90 |  48 +++
 gcc/testsuite/gfortran.dg/pr99350.f90 |   2 +-
 3 files changed, 143 insertions(+), 41 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/arithmetic_overflow_3.f90

diff --git a/gcc/fortran/arith.cc b/gcc/fortran/arith.cc
index d17d1aaa1d9..b373c25e5e1 100644
--- a/gcc/fortran/arith.cc
+++ b/gcc/fortran/arith.cc
@@ -130,6 +130,30 @@ gfc_arith_error (arith code)
 }


+/* Check if a certain arithmetic error code is severe enough to prevent
+   further simplification, as opposed to errors thrown by the range check
+   (e.g. overflow) or arithmetic exceptions that are tolerated with
+   -fno-range-check.  */
+
+static bool
+is_hard_arith_error (arith code)
+{
+  switch (code)
+{
+case ARITH_OK:
+case ARITH_OVERFLOW:
+case ARITH_UNDERFLOW:
+case ARITH_NAN:
+case ARITH_DIV0:
+case ARITH_ASYMMETRIC:
+  return false;
+
+default:
+  return true;
+}
+}
+
+
 /* Get things ready to do math.  */

 void
@@ -579,10 +603,10 @@ check_result (arith rc, gfc_expr *x, gfc_expr *r, gfc_expr **rp)
   val = ARITH_OK;
 }

-  if (val == ARITH_OK || val == ARITH_OVERFLOW)
-*rp = r;
-  else
+  if (is_hard_arith_error (val))
 gfc_free_expr (r);
+  else
+*rp = r;

   return val;
 }
@@ -792,23 +816,26 @@ gfc_arith_divide (gfc_expr *op1, gfc_expr *op2, gfc_expr **resultp)
   break;

 case BT_REAL:
-  if (mpfr_sgn (op2->value.real) == 0 && flag_range_check == 1)
-	{
-	  rc = ARITH_DIV0;
-	  break;
-	}
+  /* Set "Division by zero" only for regular numerator.  */
+  if (flag_range_check == 1
+	  && mpfr_zero_p (op2->value.real)
+	  && mpfr_regular_p (op1->value.real))
+	rc = ARITH_DIV0;

   mpfr_div (result->value.real, op1->value.real, op2->value.real,
 	   GFC_RND_MODE);
   break;

 case BT_COMPLEX:
-  if (mpc_cmp_si_si (op2->value.complex, 0, 0) == 0
-	  && flag_range_check == 1)
-	{
-	  rc = ARITH_DIV0;
-	  break;
-	}
+  /* Set "Division by zero" only for regular numerator.  */
+  if (flag_range_check == 1
+	  && mpfr_zero_p (mpc_realref (op2->value.complex))
+	  && mpfr_zero_p (mpc_imagref (op2->value.complex))
+	  && ((mpfr_regular_p (mpc_realref (op1->value.complex))
+	   && mpfr_number_p (mpc_imagref (op1->value.complex)))
+	  || (mpfr_regular_p (mpc_imagref (op1->value.complex))
+		  && mpfr_number_p (mpc_realref (op1->value.complex)
+	rc = ARITH_DIV0;

   gfc_set_model (mpc_realref (op1->value.complex));
   if