threads / discuss / 20062

found a resource leak in file builtin-fast-export.c

Subject: found a resource leak in file builtin-fast-export.c

## tl;dr

10 messages between Jul 9, 2009 and Jul 13, 2009.

replies: 9people: 6as markdown or json

Martin Ettl· Jul 9, 2009, 07:57 UTC · lore
Hi,
i have checked the source base of git with the static code analyis tool cppcheck. It brougt up an issue in file 1.6.3.3/builtin-fast-export.c at line 447.
The tool printed the following waring:
[git-1.6.3.3/builtin-fast-export.c:447]: (error) Resource leak: f
I have attached a patch to resolve this.
Best regards
Ettl Martin
-- 
Neu: GMX Doppel-FLAT mit Internet-Flatrate + Telefon-Flatrate
für nur 19,99 Euro/mtl.!* http://portal.gmx.net/de/go/dsl02


--- git-1.6.3.3/builtin-fast-export.c	2009-06-22 08:24:25.000000000 +0200
+++ git-1.6.3.3/builtin-fast-export_new.c	2009-07-09 09:44:28.000000000 +0200
@@ -442,8 +442,9 @@ static void export_marks(char *file)
 		deco++;
 	}
 
-	if (ferror(f) || fclose(f))
+	if (ferror(f))
 		error("Unable to write marks file %s.", file);
+  	fclose(f);
 }
 
 static void import_marks(char *input_file)
Thomas Rast· Jul 9, 2009, 08:31 UTC · re: Martin Ettl · lore

Re: found a resource leak in file builtin-fast-export.c

Hi Martin
Martin Ettl wrote:
> 
> I have attached a patch to resolve this.

Please read Documentation/SubmittingPatches in the source tree. And use git to track git.git!

As for the actual patch:
Show 10 quoted lines
> --- git-1.6.3.3/builtin-fast-export.c	2009-06-22 08:24:25.000000000 +0200
> +++ git-1.6.3.3/builtin-fast-export_new.c	2009-07-09 09:44:28.000000000 +0200
> @@ -442,8 +442,9 @@ static void export_marks(char *file)
>  		deco++;
>  	}
>  
> -	if (ferror(f) || fclose(f))
> +	if (ferror(f))
>  		error("Unable to write marks file %s.", file);
> +  	fclose(f);

You no longer check the error returned by fclose(). This is important, because the FILE* API may buffer writes, and a write error may only become apparent when fclose() flushes the file.

>  }
>  
>  static void import_marks(char *input_file)
-- 
Thomas Rast
trast@{inf,student}.ethz.ch
Johannes Schindelin· Jul 9, 2009, 11:04 UTC · re: Thomas Rast · lore

Re: found a resource leak in file builtin-fast-export.c

Hi,
On Thu, 9 Jul 2009, Thomas Rast wrote:
Show 8 quoted lines
> Martin Ettl wrote:
> > 
> > I have attached a patch to resolve this.
> 
> Please read Documentation/SubmittingPatches in the source tree.  And
> use git to track git.git!
> 
> As for the actual patch:
Thanks for inlining it and sparing me (and others) the hassle.
Show 14 quoted lines
> > --- git-1.6.3.3/builtin-fast-export.c	2009-06-22 08:24:25.000000000 +0200
> > +++ git-1.6.3.3/builtin-fast-export_new.c	2009-07-09 09:44:28.000000000 +0200
> > @@ -442,8 +442,9 @@ static void export_marks(char *file)
> >  		deco++;
> >  	}
> >  
> > -	if (ferror(f) || fclose(f))
> > +	if (ferror(f))
> >  		error("Unable to write marks file %s.", file);
> > +  	fclose(f);
> 
> You no longer check the error returned by fclose().  This is
> important, because the FILE* API may buffer writes, and a write error
> may only become apparent when fclose() flushes the file.

Indeed. A better fix would be to replace the || by a |, but this must be accompanied by a comment so it does not get removed due to overzealous compiler warnings.

Ciao, Dscho

Thomas Rast· Jul 9, 2009, 11:24 UTC · re: Johannes Schindelin · lore

Re: found a resource leak in file builtin-fast-export.c

Johannes Schindelin wrote:
Show 15 quoted lines
> On Thu, 9 Jul 2009, Thomas Rast wrote:
> 
> > Martin Ettl wrote:
> > > -	if (ferror(f) || fclose(f))
> > > +	if (ferror(f))
> > >  		error("Unable to write marks file %s.", file);
> > > +  	fclose(f);
> > 
> > You no longer check the error returned by fclose().  This is
> > important, because the FILE* API may buffer writes, and a write error
> > may only become apparent when fclose() flushes the file.
> 
> Indeed.  A better fix would be to replace the || by a |, but this must be 
> accompanied by a comment so it does not get removed due to overzealous 
> compiler warnings.

Are you allowed to do that? IIRC using | no longer guarantees that ferror() is called before fclose(), and my local 'man 3p fclose' says that

       After the call to fclose(), any use of stream results in
       undefined behavior.
-- 
Thomas Rast
trast@{inf,student}.ethz.ch
Andreas Ericsson· Jul 9, 2009, 11:30 UTC · re: Thomas Rast · lore

Re: found a resource leak in file builtin-fast-export.c

Thomas Rast wrote:
Show 22 quoted lines
> Johannes Schindelin wrote:
>> On Thu, 9 Jul 2009, Thomas Rast wrote:
>>
>>> Martin Ettl wrote:
>>>> -	if (ferror(f) || fclose(f))
>>>> +	if (ferror(f))
>>>>  		error("Unable to write marks file %s.", file);
>>>> +  	fclose(f);
>>> You no longer check the error returned by fclose().  This is
>>> important, because the FILE* API may buffer writes, and a write error
>>> may only become apparent when fclose() flushes the file.
>> Indeed.  A better fix would be to replace the || by a |, but this must be 
>> accompanied by a comment so it does not get removed due to overzealous 
>> compiler warnings.
> 
> Are you allowed to do that?  IIRC using | no longer guarantees that
> ferror() is called before fclose(), and my local 'man 3p fclose' says
> that
> 
>        After the call to fclose(), any use of stream results in
>        undefined behavior.
> 

A more important question; Do we really care? I haven't looked closely at the code, but afair the marks file is written once per invocation, so leaking its file descriptor sounds like something we won't really bother about.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

Considering the successes of the wars on alcohol, poverty, drugs and
terror, I think we should give some serious thought to declaring war
on peace.
Johannes Schindelin· Jul 9, 2009, 13:01 UTC · re: Thomas Rast · lore

Re: found a resource leak in file builtin-fast-export.c

Hi,
On Thu, 9 Jul 2009, Thomas Rast wrote:
Show 23 quoted lines
> Johannes Schindelin wrote:
> > On Thu, 9 Jul 2009, Thomas Rast wrote:
> > 
> > > Martin Ettl wrote:
> > > > -	if (ferror(f) || fclose(f))
> > > > +	if (ferror(f))
> > > >  		error("Unable to write marks file %s.", file);
> > > > +  	fclose(f);
> > > 
> > > You no longer check the error returned by fclose().  This is
> > > important, because the FILE* API may buffer writes, and a write error
> > > may only become apparent when fclose() flushes the file.
> > 
> > Indeed.  A better fix would be to replace the || by a |, but this must be 
> > accompanied by a comment so it does not get removed due to overzealous 
> > compiler warnings.
> 
> Are you allowed to do that?  IIRC using | no longer guarantees that
> ferror() is called before fclose(), and my local 'man 3p fclose' says
> that
> 
>        After the call to fclose(), any use of stream results in
>        undefined behavior.
Good point.  So we really need something like
	err = ferror(f);
	err |= fclose(f); /* call fclose() even if there was an error */
	if (err)
		error...

Ciao, Dscho

Matthias Andree· Jul 9, 2009, 13:28 UTC · re: Johannes Schindelin · lore

[PATCH] Fix export_marks() error handling.

- Don't leak one FILE * on error per export_marks() call. Found with
  cppcheck and reported by Martin Ettl.
- Abort the potentially long for(;idnums.size;) loop on write errors.
- Add a trailing full-stop to error message when fopen() fails.
Signed-off-by: Matthias Andree <matthias.andree@gmx.de>
---
 builtin-fast-export.c |   15 ++++++++++++---
 1 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
index 9a8a6fc..6c0956d 100644
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -428,21 +428,30 @@ static void export_marks(char *file)
 	uint32_t mark;
 	struct object_decoration *deco = idnums.hash;
 	FILE *f;
+	int e;
 
 	f = fopen(file, "w");
 	if (!f)
-		error("Unable to open marks file %s for writing", file);
+		error("Unable to open marks file %s for writing.", file);
 
 	for (i = 0; i < idnums.size; i++) {
 		if (deco->base && deco->base->type == 1) {
 			mark = ptr_to_mark(deco->decoration);
-			fprintf(f, ":%"PRIu32" %s\n", mark,
+			e = fprintf(f, ":%"PRIu32" %s\n", mark,
 				sha1_to_hex(deco->base->sha1));
+			if (e < 0) break;
 		}
 		deco++;
 	}
 
-	if (ferror(f) || fclose(f))
+	/* do not optimize the next two lines - they must both be executed in
+	 * this order. || might short-circuit the fclose(), and combining them
+	 * into one statement might reverse the order of execution.
+	 * Also, fflush() may not be sufficient - on some file systems, the
+	 * error is still delayed until the final [f]close().  */
+	e  = ferror(f);
+	e |= fclose(f);
+	if (e)
 		error("Unable to write marks file %s.", file);
 }
 
-- 
1.6.3.3.385.g60647
Stephen R. van den Berg· Jul 11, 2009, 09:45 UTC · re: Matthias Andree · lore

Re: [PATCH] Fix export_marks() error handling.

Matthias Andree wrote:
Show 8 quoted lines
>+	/* do not optimize the next two lines - they must both be executed in
>+	 * this order. || might short-circuit the fclose(), and combining them
>+	 * into one statement might reverse the order of execution.
>+	 * Also, fflush() may not be sufficient - on some file systems, the
>+	 * error is still delayed until the final [f]close().  */
>+	e  = ferror(f);
>+	e |= fclose(f);
>+	if (e)

The commentary above should be common knowledge for anyone familiar with ANSI C. So I'd suggest moving the comments into the description section of the commit and removing them from the actual code.

-- 
Sincerely,
           Stephen R. van den Berg.

"And now for something *completely* different!"
Matthias Andree· Jul 13, 2009, 08:01 UTC · re: Stephen R. van den Berg · lore

Re: [PATCH] Fix export_marks() error handling.

Am 11.07.2009, 11:45 Uhr, schrieb Stephen R. van den Berg <srb@cuci.nl>:
Show 14 quoted lines
> Matthias Andree wrote:
>> +	/* do not optimize the next two lines - they must both be executed in
>> +	 * this order. || might short-circuit the fclose(), and combining them
>> +	 * into one statement might reverse the order of execution.
>> +	 * Also, fflush() may not be sufficient - on some file systems, the
>> +	 * error is still delayed until the final [f]close().  */
>> +	e  = ferror(f);
>> +	e |= fclose(f);
>> +	if (e)
>
> The commentary above should be common knowledge for anyone familiar with
> ANSI C.  So I'd suggest moving the comments into the description section  
> of
> the commit and removing them from the actual code.

Feel free to do it and submit a patch, I'm not going to invest more time into a piece of code that runs seldomly.

-- 
Matthias Andree
Matthias Andree· Jul 9, 2009, 13:36 UTC · re: Johannes Schindelin · lore

Re: found a resource leak in file builtin-fast-export.c

Am 09.07.2009, 15:01 Uhr, schrieb Johannes Schindelin <Johannes.Schindelin@gmx.de>:

Show 36 quoted lines
> Hi,
>
> On Thu, 9 Jul 2009, Thomas Rast wrote:
>
>> Johannes Schindelin wrote:
>> > On Thu, 9 Jul 2009, Thomas Rast wrote:
>> >
>> > > Martin Ettl wrote:
>> > > > -	if (ferror(f) || fclose(f))
>> > > > +	if (ferror(f))
>> > > >  		error("Unable to write marks file %s.", file);
>> > > > +  	fclose(f);
>> > >
>> > > You no longer check the error returned by fclose().  This is
>> > > important, because the FILE* API may buffer writes, and a write  
>> error
>> > > may only become apparent when fclose() flushes the file.
>> >
>> > Indeed.  A better fix would be to replace the || by a |, but this  
>> must be
>> > accompanied by a comment so it does not get removed due to overzealous
>> > compiler warnings.
>>
>> Are you allowed to do that?  IIRC using | no longer guarantees that
>> ferror() is called before fclose(), and my local 'man 3p fclose' says
>> that
>>
>>        After the call to fclose(), any use of stream results in
>>        undefined behavior.
>
> Good point.  So we really need something like
>
> 	err = ferror(f);
> 	err |= fclose(f); /* call fclose() even if there was an error */
> 	if (err)
> 		error...

I've made such a patch, to appear soon on the list (sorry for not Cc:'ing it).

-- 
Matthias Andree

← back to recent threads