From 6568d364f4d21df69027319f55bcc903bfb32e0a Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 10 Jul 2021 17:34:39 -0700 Subject: [PATCH 10/44] sed: avoid potential double-fclose Upon a failed temp file fclose, do_ck_fclose would call panic, which would then attempt to fclose and unlink that same pointer. Caught by gcc's new -Wanalyzer-double-fclose. * sed/utils.c (struct open_file) [fclose_failed]: New member. (panic): Don't double-close. (register_open_file): Clear new member. (mark_as_fclose_failed): Use new member to avoid double fclose. (do_ck_fclose): Call mark_as_fclose_failed upon fclose failure. --- sed/utils.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/sed/utils.c b/sed/utils.c index 699fd85..98b6333 100644 --- a/sed/utils.c +++ b/sed/utils.c @@ -47,6 +47,7 @@ struct open_file char *name; struct open_file *link; unsigned temp : 1; + unsigned fclose_failed : 1; }; static struct open_file *open_files = NULL; @@ -70,7 +71,8 @@ panic (const char *str, ...) { if (open_files->temp) { - fclose (open_files->fp); + if (!open_files->fclose_failed) + fclose (open_files->fp); errno = 0; unlink (open_files->name); if (errno != 0) @@ -131,6 +133,7 @@ register_open_file (FILE *fp, const char *name) p->name = xstrdup (name); p->fp = fp; p->temp = false; + p->fclose_failed = false; } /* Panic on failing fopen */ @@ -252,6 +255,21 @@ ck_fflush (FILE *stream) panic ("couldn't flush %s: %s", utils_fp_name (stream), strerror (errno)); } +/* If we've failed to close a file in open_files whose "fp" member + is the same as FP, mark its entry as fclose_failed. */ +static void +mark_as_fclose_failed (FILE *fp) +{ + for (struct open_file *p = open_files; p; p = p->link) + { + if (p->fp == fp) + { + p->fclose_failed = true; + break; + } + } +} + /* Panic on failing fclose */ void ck_fclose (FILE *stream) @@ -293,7 +311,13 @@ do_ck_fclose (FILE *fp) clearerr (fp); if (fclose (fp) == EOF) - panic ("couldn't close %s: %s", utils_fp_name (fp), strerror (errno)); + { + /* Mark as already fclose-failed, so we don't attempt to fclose it + a second time via panic. */ + mark_as_fclose_failed (fp); + + panic ("couldn't close %s: %s", utils_fp_name (fp), strerror (errno)); + } } /* Follow symlink and panic if something fails. Return the ultimate -- 2.27.0