diff mbox series

[05/20] clone: use free() instead of UNLEAK()

Message ID patch-05.20-49e6714939d-20221228T175512Z-avarab@gmail.com (mailing list archive)
State Superseded
Headers show
Series leak fixes: various simple leak fixes | expand

Commit Message

Ævar Arnfjörð Bjarmason Dec. 28, 2022, 6 p.m. UTC
Change an UNLEAK() added in 0c4542738e6 (clone: free or UNLEAK further
pointers when finished, 2021-03-14) to use a "to_free" pattern
instead. In this case the "repo" can be either this absolute_pathdup()
value, or in the "else if" branch seen in the context the the
"argv[0]" argument to "main()".

We can only free() the value in the former case, hence the "to_free"
pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/clone.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

Comments

René Scharfe Dec. 28, 2022, 6:20 p.m. UTC | #1
Am 28.12.22 um 19:00 schrieb Ævar Arnfjörð Bjarmason:
> Change an UNLEAK() added in 0c4542738e6 (clone: free or UNLEAK further
> pointers when finished, 2021-03-14) to use a "to_free" pattern
> instead. In this case the "repo" can be either this absolute_pathdup()
> value, or in the "else if" branch seen in the context the the
> "argv[0]" argument to "main()".
>
> We can only free() the value in the former case, hence the "to_free"
> pattern.
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/clone.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/clone.c b/builtin/clone.c
> index f518bb2dc1f..48156a4f2c2 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -892,6 +892,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  	int is_bundle = 0, is_local;
>  	int reject_shallow = 0;
>  	const char *repo_name, *repo, *work_tree, *git_dir;
> +	char *repo_to_free = NULL;
>  	char *path = NULL, *dir, *display_repo = NULL;
>  	int dest_exists, real_dest_exists = 0;
>  	const struct ref *refs, *remote_head;
> @@ -949,7 +950,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  	path = get_repo_path(repo_name, &is_bundle);
>  	if (path) {
>  		FREE_AND_NULL(path);
> -		repo = absolute_pathdup(repo_name);
> +		repo = repo_to_free = absolute_pathdup(repo_name);
>  	} else if (strchr(repo_name, ':')) {
>  		repo = repo_name;

Alternatively you could do "repo = xstrdup(repo_name);" here to simplify
memory ownership of this string, at the cost of a small allocation.  But
the approach taken by this patch is fine as well.

>  		display_repo = transport_anonymize_url(repo);
> @@ -1392,7 +1393,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  	free(unborn_head);
>  	free(dir);
>  	free(path);
> -	UNLEAK(repo);
> +	free(repo_to_free);
>  	junk_mode = JUNK_LEAVE_ALL;
>
>  	transport_ls_refs_options_release(&transport_ls_refs_options);
Junio C Hamano Dec. 29, 2022, 9:02 a.m. UTC | #2
Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Change an UNLEAK() added in 0c4542738e6 (clone: free or UNLEAK further
> pointers when finished, 2021-03-14) to use a "to_free" pattern
> instead. In this case the "repo" can be either this absolute_pathdup()
> value, or in the "else if" branch seen in the context the the
> "argv[0]" argument to "main()".
>
> We can only free() the value in the former case, hence the "to_free"
> pattern.

Many other patches in the series, especially the ones that touch the
library-ish parts that can be called unbounded number of times, do
make sense, but this patch helps nobody.  Not even the leak checker,
who should already be happy with the existing UNLEAK() marking.  The
only thing it does is to free() a piece of memory obtained from a
one-time allocation that will be discarded upon program exit anyway.

If this were a freestanding patch done outside any series, I would
probably have not bothered to queue it.  I'll be queuing as part of
the larger whole, but I am not all that impressed by this step and
the thinking that led to its inclusion in the series.

Thanks.




>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/clone.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/clone.c b/builtin/clone.c
> index f518bb2dc1f..48156a4f2c2 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -892,6 +892,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  	int is_bundle = 0, is_local;
>  	int reject_shallow = 0;
>  	const char *repo_name, *repo, *work_tree, *git_dir;
> +	char *repo_to_free = NULL;
>  	char *path = NULL, *dir, *display_repo = NULL;
>  	int dest_exists, real_dest_exists = 0;
>  	const struct ref *refs, *remote_head;
> @@ -949,7 +950,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  	path = get_repo_path(repo_name, &is_bundle);
>  	if (path) {
>  		FREE_AND_NULL(path);
> -		repo = absolute_pathdup(repo_name);
> +		repo = repo_to_free = absolute_pathdup(repo_name);
>  	} else if (strchr(repo_name, ':')) {
>  		repo = repo_name;
>  		display_repo = transport_anonymize_url(repo);
> @@ -1392,7 +1393,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
>  	free(unborn_head);
>  	free(dir);
>  	free(path);
> -	UNLEAK(repo);
> +	free(repo_to_free);
>  	junk_mode = JUNK_LEAVE_ALL;
>  
>  	transport_ls_refs_options_release(&transport_ls_refs_options);
Ævar Arnfjörð Bjarmason Dec. 29, 2022, 10:20 a.m. UTC | #3
On Thu, Dec 29 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> Change an UNLEAK() added in 0c4542738e6 (clone: free or UNLEAK further
>> pointers when finished, 2021-03-14) to use a "to_free" pattern
>> instead. In this case the "repo" can be either this absolute_pathdup()
>> value, or in the "else if" branch seen in the context the the
>> "argv[0]" argument to "main()".
>>
>> We can only free() the value in the former case, hence the "to_free"
>> pattern.
>
> Many other patches in the series, especially the ones that touch the
> library-ish parts that can be called unbounded number of times, do
> make sense, but this patch helps nobody.  Not even the leak checker,
> who should already be happy with the existing UNLEAK() marking.  The
> only thing it does is to free() a piece of memory obtained from a
> one-time allocation that will be discarded upon program exit anyway.

The changes in this series that deal with UNLEAK() take it as a given
that it's a good thing to replace these trivial cases with a free().

But the rationale is in ac95f5d36ad (built-ins: use free() not UNLEAK()
if trivial, rm dead code, 2022-11-08) in the earlier topic, this is the
follow-up.

We only have 15 UNLEAK() invocations in-tree left, with this series
(which made no special effort to get the rest, but just the remaining
very low hanging fruit) it's 13.

If our built-ins were using this pattern consistently I think it would
be worth keeping & using UNLEAK() like this, but they aren't.

Even the "display_repo" variable seen in the context of this patch is
free()'d, see 46da295a770 (clone/fetch: anonymize URLs in the reflog,
2020-06-04), along with the rest in this function and others (with the
exception of the remaining UNLEAK(...)), e.g. "remote_name" etc.

So I think keeping it just makes the code more confusing, one wonders
why this particular variable is being UNLEAK()'d, instead of just
free()'d like the rest.

As the comment on the SUPPRESS_ANNOTATED_LEAKS macro notes we only get
the benefit from it if you compile with SUPPRESS_ANNOTATED_LEAKS, so
e.g. if you use SANITIZE=leak it'll kick in, but not on a normal build
and e.g.:

	valgrind --leak-check=full ./git commit-graph verify

Which will with UNLEAK() report the leak fixed in 04/20 here (I'm just
using it in this example since easier to reproduce), but with free() we
won't report it.

I agree it has a marginal benefit, e.g. it won't contribute to the
linux-leaks job, as it uses SUPPRESS_ANNOTATED_LEAKS, but the above is
why I think it's worth changing the remaining low-hanging UNLEAK() fruit
to free().

We do have cases where it's much tricker to replace those UNLEAK() with
free(). I think it's much better if we narrow its use to those cases at
this point.
diff mbox series

Patch

diff --git a/builtin/clone.c b/builtin/clone.c
index f518bb2dc1f..48156a4f2c2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -892,6 +892,7 @@  int cmd_clone(int argc, const char **argv, const char *prefix)
 	int is_bundle = 0, is_local;
 	int reject_shallow = 0;
 	const char *repo_name, *repo, *work_tree, *git_dir;
+	char *repo_to_free = NULL;
 	char *path = NULL, *dir, *display_repo = NULL;
 	int dest_exists, real_dest_exists = 0;
 	const struct ref *refs, *remote_head;
@@ -949,7 +950,7 @@  int cmd_clone(int argc, const char **argv, const char *prefix)
 	path = get_repo_path(repo_name, &is_bundle);
 	if (path) {
 		FREE_AND_NULL(path);
-		repo = absolute_pathdup(repo_name);
+		repo = repo_to_free = absolute_pathdup(repo_name);
 	} else if (strchr(repo_name, ':')) {
 		repo = repo_name;
 		display_repo = transport_anonymize_url(repo);
@@ -1392,7 +1393,7 @@  int cmd_clone(int argc, const char **argv, const char *prefix)
 	free(unborn_head);
 	free(dir);
 	free(path);
-	UNLEAK(repo);
+	free(repo_to_free);
 	junk_mode = JUNK_LEAVE_ALL;
 
 	transport_ls_refs_options_release(&transport_ls_refs_options);