diff mbox

[libgfortran] PR65089 FAIL: gfortran.dg/io_real_boz(2|_[45]).f90 when tested with -fsanitize=address

Message ID 552AE99E.5030802@charter.net
State New
Headers show

Commit Message

Jerry DeLisle April 12, 2015, 9:54 p.m. UTC
Hi all,

The attached patch fixes this bug.  It also eliminates non-freed memory whenever
a format error occurs.  Particular important when the user has use IOSTAT and 
generate_error does not exit, but returns to continue program execution.

Regression tested on x86-64 and tested with -fsanitize=address and valgrind on a 
number of existing test cases.  Changelog is fairly clear I think.

OK for trunk?

Regards,

Jerry

2015-04-12 Jerry DeLisle  <jvdelisle@gcc.gnu.org>

	PR libgfortran/65089
	* io/format.h (free_format): New function to free memory
	allocated for building format error messages.
	* io/format.c (format_error): Add checks before freeing memory
	to avoid potential segfaults and free formatting data when
	needed on error conditions. Always allocate and NULL terminate
	the string.
	* io/transfer.c (st_read_done, st_write_done): Use new
	free_format function to clean up memory allocations when done.

Comments

Janne Blomqvist April 13, 2015, 6:32 p.m. UTC | #1
On Mon, Apr 13, 2015 at 12:54 AM, Jerry DeLisle <jvdelisle@charter.net> wrote:
> Hi all,
>
> The attached patch fixes this bug.  It also eliminates non-freed memory
> whenever
> a format error occurs.  Particular important when the user has use IOSTAT
> and generate_error does not exit, but returns to continue program execution.
>
> Regression tested on x86-64 and tested with -fsanitize=address and valgrind
> on a number of existing test cases.  Changelog is fairly clear I think.
>
> OK for trunk?

Instead of xmalloc + memcpy + null-terminating, please use fc_strdup
(or fc_strdup_notrim if you want to retain trailing spaces) in order
to create a heap-allocated null-terminated copy of a
non-null-terminated string.

WIth that change, Ok for trunk and affected branches.

Since it doesn't seem to be ├╝ber-critical, I suggest you wait with
backporting to the 5.x branch until after the imminent release of 5.1
(if I understand the new numbering scheme correctly).

Thanks.
diff mbox

Patch

Index: io/format.c
===================================================================
--- io/format.c	(revision 222011)
+++ io/format.c	(working copy)
@@ -243,6 +243,18 @@  get_fnode (format_data *fmt, fnode **head, fnode *
 }
 
 
+/* free_format()-- Free allocated format string.  */
+void
+free_format (st_parameter_dt *dtp)
+{
+  if ((dtp->common.flags & IOPARM_DT_HAS_FORMAT && dtp->format))
+    {
+      free (dtp->format);
+      dtp->format = NULL;
+    }
+}
+
+
 /* free_format_data()-- Free all allocated format data.  */
 
 void
@@ -1145,7 +1157,8 @@  format_error (st_parameter_dt *dtp, const fnode *f
 
   p = strchr (buffer, '\0');
 
-  memcpy (p, dtp->format, width);
+  if (dtp->format)
+    memcpy (p, dtp->format, width);
 
   p += width;
   *p++ = '\n';
@@ -1158,6 +1171,26 @@  format_error (st_parameter_dt *dtp, const fnode *f
   *p++ = '^';
   *p = '\0';
 
+  /* Cleanup any left over memory allocations before calling generate
+     error.  */
+  if (is_internal_unit (dtp))
+    {
+      if (dtp->format != NULL)
+	{
+	  free (dtp->format);
+	  dtp->format = NULL;
+	}
+
+      /* Leave these alone if IOSTAT was given because execution will
+	 return from generate error in those cases.  */
+      if (!(dtp->common.flags & IOPARM_HAS_IOSTAT))
+	{
+	  free (dtp->u.p.fmt);
+	  free_format_hash_table (dtp->u.p.current_unit);
+	  free_internal_unit (dtp);
+	}
+    }
+
   generate_error (&dtp->common, LIBERROR_FORMAT, buffer);
 }
 
@@ -1218,13 +1251,10 @@  parse_format (st_parameter_dt *dtp)
 
   /* Not found so proceed as follows.  */
 
-  if (format_cache_ok)
-    {
-      char *fmt_string = xmalloc (dtp->format_len + 1);
-      memcpy (fmt_string, dtp->format, dtp->format_len);
-      dtp->format = fmt_string;
-      dtp->format[dtp->format_len] = '\0';
-    }
+  char *fmt_string = xmalloc (dtp->format_len + 1);
+  memcpy (fmt_string, dtp->format, dtp->format_len);
+  dtp->format = fmt_string;
+  dtp->format[dtp->format_len] = '\0';
 
   dtp->u.p.fmt = fmt = xmalloc (sizeof (format_data));
   fmt->format_string = dtp->format;
@@ -1256,19 +1286,13 @@  parse_format (st_parameter_dt *dtp)
   else
     fmt->error = "Missing initial left parenthesis in format";
 
-  if (fmt->error)
-    {
-      format_error (dtp, NULL, fmt->error);
-      if (format_cache_ok)
-	free (dtp->format);
-      free_format_hash_table (dtp->u.p.current_unit);
-      return;
-    }
-
   if (format_cache_ok)
     save_parsed_format (dtp);
   else
     dtp->u.p.format_not_saved = 1;
+
+  if (fmt->error)
+    format_error (dtp, NULL, fmt->error);
 }
 
 
Index: io/format.h
===================================================================
--- io/format.h	(revision 222011)
+++ io/format.h	(working copy)
@@ -132,6 +132,9 @@  internal_proto(format_error);
 extern void free_format_data (struct format_data *);
 internal_proto(free_format_data);
 
+extern void free_format (st_parameter_dt *);
+internal_proto(free_format);
+
 extern void free_format_hash_table (gfc_unit *);
 internal_proto(free_format_hash_table);
 
Index: io/transfer.c
===================================================================
--- io/transfer.c	(revision 222011)
+++ io/transfer.c	(working copy)
@@ -3711,9 +3711,15 @@  void
 st_read_done (st_parameter_dt *dtp)
 {
   finalize_transfer (dtp);
+  
   if (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
-    free_format_data (dtp->u.p.fmt);
+    {
+      free_format_data (dtp->u.p.fmt);
+      free_format (dtp);
+    }
+
   free_ionml (dtp);
+
   if (dtp->u.p.current_unit != NULL)
     unlock_unit (dtp->u.p.current_unit);
 
@@ -3764,8 +3770,13 @@  st_write_done (st_parameter_dt *dtp)
       }
 
   if (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
-    free_format_data (dtp->u.p.fmt);
+    {
+      free_format_data (dtp->u.p.fmt);
+      free_format (dtp);
+    }
+
   free_ionml (dtp);
+
   if (dtp->u.p.current_unit != NULL)
     unlock_unit (dtp->u.p.current_unit);