Dear all,

the attached simple and obvious patch fixes a too short copy of
the internal representation of an expression holding a string
when the string contained one or more NULs.  Such can happen with
TRANSFER of a short SOURCE to a longer MOLD of type CHARACTER.
Detected by running f951 under valgrind, see PR.

Regtested on x86_64-pc-linux-gnu.

Committed after Jerry OK'ed it in the PR.

Thanks,
Harald

From 7099a54b01a0cd04247fe88e3545916ab5ee0d22 Mon Sep 17 00:00:00 2001
From: Harald Anlauf <[email protected]>
Date: Tue, 20 Oct 2026 20:29:19 +0200
Subject: [PATCH] fortran: Prevent gfc_copy_expr() truncating strings
 containing NUL [PR127892]

gfc_copy_expr() would create a truncated copy of the internal
representation of a string when it contained a NUL character.  This can
occur while simplifying TRANSFER when the physical representation of its
result is longer than that of SOURCE, and MOLD is of type CHARACTER.
This lead to an invalid read reported by valgrind.

	PR fortran/127892

gcc/fortran/ChangeLog:

	* expr.cc (gfc_copy_expr): Ensure that the result representation
	length is the same as that of the source.
	* gfortran.h (gfc_char_to_widechar): Adjust prototype.
	* scanner.cc (gfc_char_to_widechar): Add optional argument for
	the result representation length, instead of deriving it from the
	string argument.
---
 gcc/fortran/expr.cc    | 3 ++-
 gcc/fortran/gfortran.h | 2 +-
 gcc/fortran/scanner.cc | 6 ++++--
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/gcc/fortran/expr.cc b/gcc/fortran/expr.cc
index 561318ed2e5..9e06866ad63 100644
--- a/gcc/fortran/expr.cc
+++ b/gcc/fortran/expr.cc
@@ -349,7 +349,8 @@ gfc_copy_expr (gfc_expr *p)
 	  if (p->representation.string
 	      && p->ts.kind == gfc_default_character_kind)
 	    q->value.character.string
-	      = gfc_char_to_widechar (q->representation.string);
+	      = gfc_char_to_widechar (q->representation.string,
+				      p->representation.length);
 	  else
 	    {
 	      s = gfc_get_wide_string (p->value.character.length + 1);
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 9d4455ec7e3..7433de7c1c8 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -3646,7 +3646,7 @@ size_t gfc_wide_strlen (const gfc_char_t *);
 int gfc_wide_strncasecmp (const gfc_char_t *, const char *, size_t);
 gfc_char_t *gfc_wide_memset (gfc_char_t *, gfc_char_t, size_t);
 char *gfc_widechar_to_char (const gfc_char_t *, int);
-gfc_char_t *gfc_char_to_widechar (const char *);
+gfc_char_t *gfc_char_to_widechar (const char *, gfc_charlen_t = -1);
 
 #define gfc_get_wide_string(n) XCNEWVEC (gfc_char_t, n)
 
diff --git a/gcc/fortran/scanner.cc b/gcc/fortran/scanner.cc
index ccd44a145d5..9b77e559065 100644
--- a/gcc/fortran/scanner.cc
+++ b/gcc/fortran/scanner.cc
@@ -203,7 +203,7 @@ gfc_widechar_to_char (const gfc_char_t *s, int length)
 }
 
 gfc_char_t *
-gfc_char_to_widechar (const char *s)
+gfc_char_to_widechar (const char *s, gfc_charlen_t length)
 {
   size_t len, i;
   gfc_char_t *res;
@@ -211,7 +211,9 @@ gfc_char_to_widechar (const char *s)
   if (s == NULL)
     return NULL;
 
-  len = strlen (s);
+  /* Passing a negative length is used to indicate that length should be
+     calculated using strlen().  */
+  len = (length >= 0 ? (size_t) length : strlen (s));
   res = gfc_get_wide_string (len + 1);
 
   for (i = 0; i < len; i++)
-- 
2.51.0

Reply via email to