diff mbox series

[2/2] object-file: free(*contents) only in read_loose_object() caller

Message ID patch-2.2-e2a5468e2e0-20211111T051800Z-avarab@gmail.com (mailing list archive)
State Accepted
Commit 16235e3b1460ddd708995b4cc5028e49d47e3761
Headers show
Series v2.34.0-rc2 regression: free() of uninitialized in ab/fsck-unexpected-type | expand

Commit Message

Ævar Arnfjörð Bjarmason Nov. 11, 2021, 5:18 a.m. UTC
In the preceding commit a free() of uninitialized memory regression in
96e41f58fe1 (fsck: report invalid object type-path combinations,
2021-10-01) was fixed, but we'd still have an issue with leaking
memory from fsck_loose(). Let's fix that issue too.

That issue was introduced in my 31deb28f5e0 (fsck: don't hard die on
invalid object types, 2021-10-01). It can be reproduced under
SANITIZE=leak with the test I added in 093fffdfbec (fsck tests: add
test for fsck-ing an unknown type, 2021-10-01):

    ./t1450-fsck.sh --run=84 -vixd

In some sense it's not a problem, we lost the same amount of memory in
terms of things malloc'd and not free'd. It just moved from the "still
reachable" to "definitely lost" column in valgrind(1) nomenclature[1],
since we'd have die()'d before.

But now that we don't hard die() anymore in the library let's properly
free() it. Doing so makes this code much easier to follow, since we'll
now have one function owning the freeing of the "contents" variable,
not two.

For context on that memory management pattern the read_loose_object()
function was added in f6371f92104 (sha1_file: add read_loose_object()
function, 2017-01-13) and subsequently used in c68b489e564 (fsck:
parse loose object paths directly, 2017-01-13). The pattern of it
being the task of both sides to free() the memory has been there in
this form since its inception.

1. https://valgrind.org/docs/manual/mc-manual.html#mc-manual.leaks

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/fsck.c | 3 ++-
 object-file.c  | 7 ++-----
 2 files changed, 4 insertions(+), 6 deletions(-)

Comments

Junio C Hamano Nov. 11, 2021, 6:54 p.m. UTC | #1
Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> diff --git a/builtin/fsck.c b/builtin/fsck.c
> index d87c28a1cc4..27b9e78094d 100644
> --- a/builtin/fsck.c
> +++ b/builtin/fsck.c
> @@ -605,7 +605,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
>  	struct object *obj;
>  	enum object_type type = OBJ_NONE;
>  	unsigned long size;
> -	void *contents;
> +	void *contents = NULL;
>  	int eaten;
>  	struct object_info oi = OBJECT_INFO_INIT;
>  	struct object_id real_oid = *null_oid();
> @@ -630,6 +630,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
>  			    path);
>  	if (err < 0) {
>  		errors_found |= ERROR_OBJECT;
> +		free(contents);
>  		return 0; /* keep checking other objects */
>  	}
>  
> diff --git a/object-file.c b/object-file.c
> index ac476653a06..c3d866a287e 100644
> --- a/object-file.c
> +++ b/object-file.c
> @@ -2528,8 +2528,6 @@ int read_loose_object(const char *path,
>  	char hdr[MAX_HEADER_LEN];
>  	unsigned long *size = oi->sizep;
>  
> -	*contents = NULL;
> -
>  	map = map_loose_object_1(the_repository, path, NULL, &mapsize);
>  	if (!map) {
>  		error_errno(_("unable to mmap %s"), path);
> @@ -2559,10 +2557,9 @@ int read_loose_object(const char *path,
>  			goto out;
>  		}
>  		if (check_object_signature(the_repository, expected_oid,
> -					   *contents, *size, oi->type_name->buf, real_oid)) {
> -			free(*contents);
> +					   *contents, *size,
> +					   oi->type_name->buf, real_oid))
>  			goto out;
> -		}
>  	}

Yeah, I have to say that read_loose_object() that frees *contents
without clearing *contents to NULL only because it wants to signal
if the failure comes from check_object_signature() step is quite
ugly.  Making the caller responsible for freeing (in other words,
when caller's *contents is non-NULL after function returns, it
always has a valid piece of memory to be freed) makes the contract
easier to explain.
diff mbox series

Patch

diff --git a/builtin/fsck.c b/builtin/fsck.c
index d87c28a1cc4..27b9e78094d 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -605,7 +605,7 @@  static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 	struct object *obj;
 	enum object_type type = OBJ_NONE;
 	unsigned long size;
-	void *contents;
+	void *contents = NULL;
 	int eaten;
 	struct object_info oi = OBJECT_INFO_INIT;
 	struct object_id real_oid = *null_oid();
@@ -630,6 +630,7 @@  static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 			    path);
 	if (err < 0) {
 		errors_found |= ERROR_OBJECT;
+		free(contents);
 		return 0; /* keep checking other objects */
 	}
 
diff --git a/object-file.c b/object-file.c
index ac476653a06..c3d866a287e 100644
--- a/object-file.c
+++ b/object-file.c
@@ -2528,8 +2528,6 @@  int read_loose_object(const char *path,
 	char hdr[MAX_HEADER_LEN];
 	unsigned long *size = oi->sizep;
 
-	*contents = NULL;
-
 	map = map_loose_object_1(the_repository, path, NULL, &mapsize);
 	if (!map) {
 		error_errno(_("unable to mmap %s"), path);
@@ -2559,10 +2557,9 @@  int read_loose_object(const char *path,
 			goto out;
 		}
 		if (check_object_signature(the_repository, expected_oid,
-					   *contents, *size, oi->type_name->buf, real_oid)) {
-			free(*contents);
+					   *contents, *size,
+					   oi->type_name->buf, real_oid))
 			goto out;
-		}
 	}
 
 	ret = 0; /* everything checks out */