diff mbox series

[v2,01/10] pack-write.c: plug a leak in stage_tmp_packfiles()

Message ID c477b754e7ddde0d6e696cfd4027ad88c18aeff3.1681850424.git.me@ttaylorr.com (mailing list archive)
State Accepted
Commit c41258359e2f77a4fba000b2bbb975ebaf7d641b
Headers show
Series gc: enable cruft packs by default | expand

Commit Message

Taylor Blau April 18, 2023, 8:40 p.m. UTC
The function `stage_tmp_packfiles()` generates a filename to use for
staging the contents of what will become the pack's ".mtimes" file.

The name is generated in `write_mtimes_file()` and the result is
returned back to `stage_tmp_packfiles()` which uses it to rename the
temporary file into place via `rename_tmp_packfiles()`.

`write_mtimes_file()` returns a `const char *`, indicating that callers
are not expected to free its result (similar to, e.g., `oid_to_hex()`).
But callers are expected to free its result, so this return type is
incorrect.

Change the function's signature to return a non-const `char *`, and free
it at the end of `stage_tmp_packfiles()`.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
 pack-write.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

Comments

Junio C Hamano April 19, 2023, 10 p.m. UTC | #1
Taylor Blau <me@ttaylorr.com> writes:

> The function `stage_tmp_packfiles()` generates a filename to use for
> staging the contents of what will become the pack's ".mtimes" file.
>
> The name is generated in `write_mtimes_file()` and the result is
> returned back to `stage_tmp_packfiles()` which uses it to rename the
> temporary file into place via `rename_tmp_packfiles()`.
>
> `write_mtimes_file()` returns a `const char *`, indicating that callers
> are not expected to free its result (similar to, e.g., `oid_to_hex()`).
> But callers are expected to free its result, so this return type is
> incorrect.

Indeed the string that holds the name of the file returned by
write_mtimes_file() is leaking.  Does the same logic apply to the
returned filename from write_rev_file() and stored in rev_tmp_name
that is not freed in stage_tmp_packfiles() in another topic?

> @@ -544,7 +544,7 @@ void stage_tmp_packfiles(struct strbuf *name_buffer,
>  			 char **idx_tmp_name)
>  {
>  	const char *rev_tmp_name = NULL;
> -	const char *mtimes_tmp_name = NULL;
> +	char *mtimes_tmp_name = NULL;
>  
>  	if (adjust_shared_perm(pack_tmp_name))
>  		die_errno("unable to make temporary pack file readable");
> @@ -568,6 +568,8 @@ void stage_tmp_packfiles(struct strbuf *name_buffer,
>  		rename_tmp_packfile(name_buffer, rev_tmp_name, "rev");
>  	if (mtimes_tmp_name)
>  		rename_tmp_packfile(name_buffer, mtimes_tmp_name, "mtimes");
> +
> +	free(mtimes_tmp_name);
>  }
>  
>  void write_promisor_file(const char *promisor_name, struct ref **sought, int nr_sought)
Taylor Blau April 20, 2023, 4:31 p.m. UTC | #2
On Wed, Apr 19, 2023 at 03:00:48PM -0700, Junio C Hamano wrote:
> Taylor Blau <me@ttaylorr.com> writes:
>
> > The function `stage_tmp_packfiles()` generates a filename to use for
> > staging the contents of what will become the pack's ".mtimes" file.
> >
> > The name is generated in `write_mtimes_file()` and the result is
> > returned back to `stage_tmp_packfiles()` which uses it to rename the
> > temporary file into place via `rename_tmp_packfiles()`.
> >
> > `write_mtimes_file()` returns a `const char *`, indicating that callers
> > are not expected to free its result (similar to, e.g., `oid_to_hex()`).
> > But callers are expected to free its result, so this return type is
> > incorrect.
>
> Indeed the string that holds the name of the file returned by
> write_mtimes_file() is leaking.  Does the same logic apply to the
> returned filename from write_rev_file() and stored in rev_tmp_name
> that is not freed in stage_tmp_packfiles() in another topic?

They are similar, but unfortunately different.

Here, our temporary name is generated by `write_mtimes_file()` which
*always* comes up with a new name from scratch. So we know that it
should always be free()'d at the end of `stage_tmp_packfiles()`.

But in the case of `write_rev_file()`, it only *sometimes* generates a
new name from scratch. The first parameter can be NULL, in which case
`write_rev_file()` will generate a new name. Or it can be non-NULL, in
which case that name will be used instead.

So in that topic, it's less clear on what the ultimate right path
forward is. But in this topic, changing `mtimes_tmp_name` and the return
type of `write_mtimes_file()` to be a non-const `char *` (and free()ing
it, of course ;-)) is the right thing to do.

Thanks,
Taylor
Junio C Hamano April 20, 2023, 4:57 p.m. UTC | #3
Taylor Blau <me@ttaylorr.com> writes:

>> Indeed the string that holds the name of the file returned by
>> write_mtimes_file() is leaking.  Does the same logic apply to the
>> returned filename from write_rev_file() and stored in rev_tmp_name
>> that is not freed in stage_tmp_packfiles() in another topic?
>
> They are similar, but unfortunately different.

I hoped "fortunately different and there is no leak", but it seems
what you said below is a bit different X-<.

> But in the case of `write_rev_file()`, it only *sometimes* generates a
> new name from scratch. The first parameter can be NULL, in which case
> `write_rev_file()` will generate a new name. Or it can be non-NULL, in
> which case that name will be used instead.

IOW, it is a mess.

But I think the topic to introduce that variable is being rerolled
and has no interaction with the codepath we are discussing here, so
we are still in good shape wrt this series ;-)

Thanks.
diff mbox series

Patch

diff --git a/pack-write.c b/pack-write.c
index f171405495..4da0ccc5f5 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -312,13 +312,13 @@  static void write_mtimes_trailer(struct hashfile *f, const unsigned char *hash)
 	hashwrite(f, hash, the_hash_algo->rawsz);
 }
 
-static const char *write_mtimes_file(struct packing_data *to_pack,
-				     struct pack_idx_entry **objects,
-				     uint32_t nr_objects,
-				     const unsigned char *hash)
+static char *write_mtimes_file(struct packing_data *to_pack,
+			       struct pack_idx_entry **objects,
+			       uint32_t nr_objects,
+			       const unsigned char *hash)
 {
 	struct strbuf tmp_file = STRBUF_INIT;
-	const char *mtimes_name;
+	char *mtimes_name;
 	struct hashfile *f;
 	int fd;
 
@@ -544,7 +544,7 @@  void stage_tmp_packfiles(struct strbuf *name_buffer,
 			 char **idx_tmp_name)
 {
 	const char *rev_tmp_name = NULL;
-	const char *mtimes_tmp_name = NULL;
+	char *mtimes_tmp_name = NULL;
 
 	if (adjust_shared_perm(pack_tmp_name))
 		die_errno("unable to make temporary pack file readable");
@@ -568,6 +568,8 @@  void stage_tmp_packfiles(struct strbuf *name_buffer,
 		rename_tmp_packfile(name_buffer, rev_tmp_name, "rev");
 	if (mtimes_tmp_name)
 		rename_tmp_packfile(name_buffer, mtimes_tmp_name, "mtimes");
+
+	free(mtimes_tmp_name);
 }
 
 void write_promisor_file(const char *promisor_name, struct ref **sought, int nr_sought)