diff mbox

[Fortran] PR60334 - Segmentation fault on character pointer assignments

Message ID 20150111162105.6fb725dc@vepi2
State New
Headers show

Commit Message

Andre Vehreschild Jan. 11, 2015, 3:21 p.m. UTC
Hi Paul,

thanks for the review. I do not have commits rights.

Unfortunately is the patch not ok. I figured today, that it needs an extension
when function calls that return deferred char len arrays are nested. In this
special case the string length would have been lost. The attached extended
version fixes this issue. 

Sorry for the duplicate work.

Bootstraps and regtests ok on x86_64-linux-gnu.

Regards,
	Andre

On Sun, 11 Jan 2015 16:11:10 +0100
Paul Richard Thomas <paul.richard.thomas@gmail.com> wrote:

> Dear Andre,
> 
> This is OK for trunk. I have not been keeping track of whether or not
> you have commit rights yet. If not, I will get to it sometime this
> week.
> 
> Thanks for the patch.
> 
> Paul
> 
> On 10 January 2015 at 15:59, Andre Vehreschild <vehre@gmx.de> wrote:
> > Hi all,
> >
> > attached patch fixes the bug reported in pr 60334. The issue here was that
> > the function's result being (a pointer to) a deferred length char array.
> > The string length for the result value was wrapped in a local variable,
> > whose value was never written back to the string length of the result. This
> > lead the calling routine to take the length of the result to be random
> > leading to a crash.
> >
> > This patch addresses the issue by preventing the instantiation of the local
> > var and instead using a reference to the parameter. This not only saves one
> > value on the stack, but also because for small functions the compiler will
> > hold all parameters in registers for a significant level of optimization,
> > all the overhead of memory access (I hope :-).
> >
> > Bootstraps and regtests ok on x86_64-linux-gnu.
> >
> > - Andre
> > --
> > Andre Vehreschild * Kreuzherrenstr. 8 * 52062 Aachen
> > Tel.: +49 241 9291018 * Email: vehre@gmx.de
> 
> 
>

Comments

Paul Richard Thomas Jan. 12, 2015, 9:07 p.m. UTC | #1
Hi Andre,

+      if (INDIRECT_REF_P (parmse.string_length))
+        /* In chains of functions/procedure calls the string_length already
+           is a pointer to the variable holding the length.  Therefore
+           remove the deref on call.  */
+        parmse.string_length = TREE_OPERAND (parmse.string_length, 0);

This is OK but I would use instead:
+      if (POINTER_TYPE_P (parmse.string_length))
+        /* In chains of functions/procedure calls the string_length already
+           is a pointer to the variable holding the length.  Therefore
+           remove the deref on call.  */
+        parmse.string_length = build_fold_indirect_ref (parmse.string_length);

If you look in ~/gcc/fold-const.c:15751, you will see that
TREE_OPERAND (parmse.string_length, 0) but that it is preceded by
cleaning up of NOOPS and, in any case, its usage will preserve the
standard API.... just in case the internals change :-)

of course, using TREE_OPERAND (xxx, 0) in the various fortran class
functions makes such an assumption ;-)

Apart from that, the patch is fine.

I'll have a session of doing some commits later this week and will do
this patch at that time.

Cheers

Paul

On 11 January 2015 at 16:21, Andre Vehreschild <vehre@gmx.de> wrote:
> Hi Paul,
>
> thanks for the review. I do not have commits rights.
>
> Unfortunately is the patch not ok. I figured today, that it needs an extension
> when function calls that return deferred char len arrays are nested. In this
> special case the string length would have been lost. The attached extended
> version fixes this issue.
>
> Sorry for the duplicate work.
>
> Bootstraps and regtests ok on x86_64-linux-gnu.
>
> Regards,
>         Andre
>
> On Sun, 11 Jan 2015 16:11:10 +0100
> Paul Richard Thomas <paul.richard.thomas@gmail.com> wrote:
>
>> Dear Andre,
>>
>> This is OK for trunk. I have not been keeping track of whether or not
>> you have commit rights yet. If not, I will get to it sometime this
>> week.
>>
>> Thanks for the patch.
>>
>> Paul
>>
>> On 10 January 2015 at 15:59, Andre Vehreschild <vehre@gmx.de> wrote:
>> > Hi all,
>> >
>> > attached patch fixes the bug reported in pr 60334. The issue here was that
>> > the function's result being (a pointer to) a deferred length char array.
>> > The string length for the result value was wrapped in a local variable,
>> > whose value was never written back to the string length of the result. This
>> > lead the calling routine to take the length of the result to be random
>> > leading to a crash.
>> >
>> > This patch addresses the issue by preventing the instantiation of the local
>> > var and instead using a reference to the parameter. This not only saves one
>> > value on the stack, but also because for small functions the compiler will
>> > hold all parameters in registers for a significant level of optimization,
>> > all the overhead of memory access (I hope :-).
>> >
>> > Bootstraps and regtests ok on x86_64-linux-gnu.
>> >
>> > - Andre
>> > --
>> > Andre Vehreschild * Kreuzherrenstr. 8 * 52062 Aachen
>> > Tel.: +49 241 9291018 * Email: vehre@gmx.de
>>
>>
>>
>
>
> --
> Andre Vehreschild * Kreuzherrenstr. 8 * 52062 Aachen
> Tel.: +49 241 9291018 * Email: vehre@gmx.de
Tobias Burnus Jan. 14, 2015, 1:57 p.m. UTC | #2
Hi Paul and Andre, hi all,

Paul Richard Thomas wrote:
...
[From Andre's patch]
> +           is a pointer to the variable holding the length.  Therefore
> +           remove the deref on call.  */
> +        parmse.string_length = TREE_OPERAND (parmse.string_length, 0);

> This is OK but I would use instead:
...
+        parmse.string_length = build_fold_indirect_ref (parmse.string_length);

That doesn't match the comment: TREE_OPERAND(..., 0) removes the dereference,
yielding a pointer, while your suggestion adds another deref.

The opposite to dereferrencing is to take the address, i.e. using
   gfc_build_addr_expr (NULL_TREE, ...)


> If you look in ~/gcc/fold-const.c:15751, you will see that
> TREE_OPERAND (parmse.string_length, 0) but that it is preceded by
> cleaning up of NOOPS and, in any case, its usage will preserve the
> standard API.... just in case the internals change :-)

I think using TREE_OPERAND directly should be fine. The condition
  if (INDIRECT_REF_P (parmse.string_length))
is only true if the last operator is an indirect ref; in that case, the object
must be a pointer.

Thus, I think TREE_OPERAND is better than gfc_build_addr_expr.


The only potential issue would be that one has the wrong type; but I think that
this is not possible in this case - and for function argument passing, using
the wrong type would be already wrong even for pass by value (instead of by
reference). [For value passing, one could use a cast/fold_convert(), for by
ref not; however, there isn't one as INDIRECT_REF_P is the outermost operator.]

Tobias

PS: I haven't looked closer at the rest of the patch.
Andre Vehreschild Jan. 14, 2015, 2:17 p.m. UTC | #3
Hi Tobias, hi Paul, hi all,

sorry, for the mess. I did not understand the meaning of
build_fold_indirect_ref in the circumstances Paul pointed out. So if no
objections exist, I like to propose the previous patch from:

https://gcc.gnu.org/ml/fortran/2015-01/msg00056.html

to address the issue in pr60334. This resolves the issue.

Regards,
	Andre

On Wed, 14 Jan 2015 14:57:57 +0100
Tobias Burnus <tobias.burnus@physik.fu-berlin.de> wrote:

> Hi Paul and Andre, hi all,
> 
> Paul Richard Thomas wrote:
> ...
> [From Andre's patch]
> > +           is a pointer to the variable holding the length.  Therefore
> > +           remove the deref on call.  */
> > +        parmse.string_length = TREE_OPERAND (parmse.string_length, 0);
> 
> > This is OK but I would use instead:
> ...
> +        parmse.string_length = build_fold_indirect_ref
> (parmse.string_length);
> 
> That doesn't match the comment: TREE_OPERAND(..., 0) removes the dereference,
> yielding a pointer, while your suggestion adds another deref.
> 
> The opposite to dereferrencing is to take the address, i.e. using
>    gfc_build_addr_expr (NULL_TREE, ...)
> 
> 
> > If you look in ~/gcc/fold-const.c:15751, you will see that
> > TREE_OPERAND (parmse.string_length, 0) but that it is preceded by
> > cleaning up of NOOPS and, in any case, its usage will preserve the
> > standard API.... just in case the internals change :-)
> 
> I think using TREE_OPERAND directly should be fine. The condition
>   if (INDIRECT_REF_P (parmse.string_length))
> is only true if the last operator is an indirect ref; in that case, the object
> must be a pointer.
> 
> Thus, I think TREE_OPERAND is better than gfc_build_addr_expr.
> 
> 
> The only potential issue would be that one has the wrong type; but I think
> that this is not possible in this case - and for function argument passing,
> using the wrong type would be already wrong even for pass by value (instead
> of by reference). [For value passing, one could use a cast/fold_convert(),
> for by ref not; however, there isn't one as INDIRECT_REF_P is the outermost
> operator.]
> 
> Tobias
> 
> PS: I haven't looked closer at the rest of the patch.
diff mbox

Patch

diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index 1e74125..86873f7 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -1333,12 +1333,30 @@  gfc_get_symbol_decl (gfc_symbol * sym)
 	     (sym->ts.u.cl->passed_length == sym->ts.u.cl->backend_decl))
 	    sym->ts.u.cl->backend_decl = NULL_TREE;
 
-	  if (sym->ts.deferred && fun_or_res
-		&& sym->ts.u.cl->passed_length == NULL
-		&& sym->ts.u.cl->backend_decl)
+	  if (sym->ts.deferred && byref)
 	    {
-	      sym->ts.u.cl->passed_length = sym->ts.u.cl->backend_decl;
-	      sym->ts.u.cl->backend_decl = NULL_TREE;
+	      /* The string length of a deferred char array is stored in the
+		 parameter at sym->ts.u.cl->backend_decl as a reference and
+		 marked as a result.  Exempt this variable from generating a
+		 temporary for it.  */
+	      if (sym->attr.result)
+		{
+		  /* We need to insert a indirect ref for param decls.  */
+		  if (sym->ts.u.cl->backend_decl
+		      && TREE_CODE (sym->ts.u.cl->backend_decl) == PARM_DECL)
+		    sym->ts.u.cl->backend_decl =
+			build_fold_indirect_ref (sym->ts.u.cl->backend_decl);
+		}
+	      /* For all other parameters make sure, that they are copied so
+		 that the value and any modifications are local to the routine
+		 by generating a temporary variable.  */
+	      else if (sym->attr.function
+		       && sym->ts.u.cl->passed_length == NULL
+		       && sym->ts.u.cl->backend_decl)
+		{
+		  sym->ts.u.cl->passed_length = sym->ts.u.cl->backend_decl;
+		  sym->ts.u.cl->backend_decl = NULL_TREE;
+		}
 	    }
 
 	  if (sym->ts.u.cl->backend_decl == NULL_TREE)
diff --git a/gcc/fortran/trans-expr.c b/gcc/fortran/trans-expr.c
index 6bd3b03..a0390c1 100644
--- a/gcc/fortran/trans-expr.c
+++ b/gcc/fortran/trans-expr.c
@@ -5058,10 +5058,18 @@  gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 	 so that the value can be returned.  */
       if (parmse.string_length && fsym && fsym->ts.deferred)
 	{
-	  tmp = parmse.string_length;
-	  if (TREE_CODE (tmp) != VAR_DECL)
-	    tmp = gfc_evaluate_now (parmse.string_length, &se->pre);
-	  parmse.string_length = gfc_build_addr_expr (NULL_TREE, tmp);
+	  if (INDIRECT_REF_P (parmse.string_length))
+	    /* In chains of functions/procedure calls the string_length already
+	       is a pointer to the variable holding the length.  Therefore
+	       remove the deref on call.  */
+	    parmse.string_length = TREE_OPERAND (parmse.string_length, 0);
+	  else
+	    {
+	      tmp = parmse.string_length;
+	      if (TREE_CODE (tmp) != VAR_DECL)
+		tmp = gfc_evaluate_now (parmse.string_length, &se->pre);
+	      parmse.string_length = gfc_build_addr_expr (NULL_TREE, tmp);
+	    }
 	}
 
       /* Character strings are passed as two parameters, a length and a
diff --git a/gcc/testsuite/gfortran.dg/deferred_type_param_6.f90 b/gcc/testsuite/gfortran.dg/deferred_type_param_6.f90
index eb00778..a2fabe8 100644
--- a/gcc/testsuite/gfortran.dg/deferred_type_param_6.f90
+++ b/gcc/testsuite/gfortran.dg/deferred_type_param_6.f90
@@ -2,15 +2,23 @@ 
 !
 ! PR fortran/51055
 ! PR fortran/49110
-!
+! PR fortran/60334
 
 subroutine test()
   implicit none
   integer :: i = 5
   character(len=:), allocatable :: s1
+  character(len=:), pointer :: s2
+  character(len=5), target :: fifeC = 'FIVEC'
   call sub(s1, i)
   if (len(s1) /= 5) call abort()
   if (s1 /= "ZZZZZ") call abort()
+  s2 => subfunc()
+  if (len(s2) /= 5) call abort()
+  if (s2 /= "FIVEC") call abort()
+  s1 = addPrefix(subfunc())
+  if (len(s1) /= 7) call abort()
+  if (s1 /= "..FIVEC") call abort()
 contains
   subroutine sub(str,j)
     character(len=:), allocatable :: str
@@ -19,6 +27,17 @@  contains
     if (len(str) /= 5) call abort()
     if (str /= "ZZZZZ") call abort()
   end subroutine sub
+  function subfunc() result(res)
+    character(len=:), pointer :: res
+    res => fifec
+    if (len(res) /= 5) call abort()
+    if (res /= "FIVEC") call abort()
+  end function subfunc
+  function addPrefix(str) result(res)
+    character(len=:), pointer :: str
+    character(len=:), allocatable :: res
+    res = ".." // str
+  end function addPrefix
 end subroutine test
 
 program a