From 7502b19d938bde2fcfc9d5976e82003349dce04f Mon Sep 17 00:00:00 2001 From: jvdelisle Date: Wed, 15 Apr 2015 01:27:03 +0000 Subject: 2015-04-14 Jerry DeLisle 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@222111 138bc75d-0d04-0410-961f-82ee72b054a4 --- libgfortran/ChangeLog | 12 ++++++++++ libgfortran/io/format.c | 56 +++++++++++++++++++++++++++++++++-------------- libgfortran/io/format.h | 3 +++ libgfortran/io/transfer.c | 15 +++++++++++-- 4 files changed, 67 insertions(+), 19 deletions(-) (limited to 'libgfortran') diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index 67bb9cce5e1..9f53ae3e8af 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -1,3 +1,15 @@ +2015-04-14 Jerry DeLisle + + 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. + 2015-03-28 Jerry DeLisle PR libgfortran/65596 diff --git a/libgfortran/io/format.c b/libgfortran/io/format.c index fa81d9b87fc..42be2586e1f 100644 --- a/libgfortran/io/format.c +++ b/libgfortran/io/format.c @@ -243,6 +243,18 @@ get_fnode (format_data *fmt, fnode **head, fnode **tail, format_token t) } +/* 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, const char *message) 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, const char *message) *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,8 @@ 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 = fc_strdup_notrim (dtp->format, dtp->format_len); + dtp->format = fmt_string; dtp->u.p.fmt = fmt = xmalloc (sizeof (format_data)); fmt->format_string = dtp->format; @@ -1256,19 +1284,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); } diff --git a/libgfortran/io/format.h b/libgfortran/io/format.h index de5cdf9ee81..11319f468d4 100644 --- a/libgfortran/io/format.h +++ b/libgfortran/io/format.h @@ -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); diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c index 7bbee2131dd..746bb6dcc6c 100644 --- a/libgfortran/io/transfer.c +++ b/libgfortran/io/transfer.c @@ -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); -- cgit v1.2.1