diff mbox series

[v2,08/24] submodule--helper: add and use *_release() functions

Message ID patch-v2-08.24-b7582391c91-20220719T204458Z-avarab@gmail.com (mailing list archive)
State Superseded
Headers show
Series submodule--helper: fix memory leaks | expand

Commit Message

Ævar Arnfjörð Bjarmason July 19, 2022, 8:46 p.m. UTC
Add release functions for "struct module_list", "struct
submodule_update_clone" and "struct update_data".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/submodule--helper.c      | 29 ++++++++++++++++++++++++++++-
 t/t6134-pathspec-in-submodule.sh |  1 +
 2 files changed, 29 insertions(+), 1 deletion(-)

Comments

Junio C Hamano July 20, 2022, 5:06 p.m. UTC | #1
Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Add release functions for "struct module_list", "struct
> submodule_update_clone" and "struct update_data".

OK, this does a lot of things, but in short, nobody bothered to free
module_list (i.e. list of cache entries borrowed from the index to
represent the paths we are interested in that are gitlinks),
update_data (which has module_list among other things that do not
need to be freed), and submodule_update_clone (which has
update_data, update_clone_data and failed_clones list) in the
original (in other words, these release helpers had to be invented
by this patch, not factored out from some codepaths that free them).

I think the earlier part of the patch that deals with module_list is
correct.  I also think the last one that clears update_data in
module_update() is correct.

I am not sure about the helper that releases suc, especially how
suc->update_data is left alone by update_submodules().  Presumably
the caller is responsible for releasing the resources held by
update_data member alone, but the interaction makes me feel dirty.
Luckily there is only one caller of suc_release(), so we can design
however dirty interface for convenience, but still it feels wrong to
design a release() helper that pretends to be usable by general
public, yet they have to be aware of the fact that some members in
the struct are still their responsibility to release.  I dunno.
Ævar Arnfjörð Bjarmason July 21, 2022, 5:44 p.m. UTC | #2
On Wed, Jul 20 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> Add release functions for "struct module_list", "struct
>> submodule_update_clone" and "struct update_data".
>
> OK, this does a lot of things, but in short, nobody bothered to free
> module_list (i.e. list of cache entries borrowed from the index to
> represent the paths we are interested in that are gitlinks),
> update_data (which has module_list among other things that do not
> need to be freed), and submodule_update_clone (which has
> update_data, update_clone_data and failed_clones list) in the
> original (in other words, these release helpers had to be invented
> by this patch, not factored out from some codepaths that free them).
>
> I think the earlier part of the patch that deals with module_list is
> correct.  I also think the last one that clears update_data in
> module_update() is correct.

...

> I am not sure about the helper that releases suc, especially how
> suc->update_data is left alone by update_submodules().  Presumably
> the caller is responsible for releasing the resources held by
> update_data member alone, but the interaction makes me feel dirty.
> Luckily there is only one caller of suc_release(), so we can design
> however dirty interface for convenience, but still it feels wrong to
> design a release() helper that pretends to be usable by general
> public, yet they have to be aware of the fact that some members in
> the struct are still their responsibility to release.  I dunno.

But isn't this fine because that "update_data" member has a "const" on
it:

	/* configuration parameters which are passed on to the children */
	const struct update_data *update_data;

I fix other similar ownership issues you pointed out for a re-roll I'm
preparing, but I don't see why this one is a problem as long as that
"const" is there, it's a slot we use for passing things in externally &
to ferry it along.
diff mbox series

Patch

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 7e9af9999bb..5d26e188560 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -182,6 +182,11 @@  struct module_list {
 };
 #define MODULE_LIST_INIT { 0 }
 
+static void module_list_release(struct module_list *ml)
+{
+	free(ml->entries);
+}
+
 static int module_list_compute(int argc, const char **argv,
 			       const char *prefix,
 			       struct pathspec *pathspec,
@@ -243,7 +248,7 @@  static void module_list_active(struct module_list *list)
 		active_modules.entries[active_modules.nr++] = ce;
 	}
 
-	free(list->entries);
+	module_list_release(list);
 	*list = active_modules;
 }
 
@@ -307,6 +312,7 @@  static int module_list(int argc, const char **argv, const char *prefix)
 	}
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -465,6 +471,7 @@  static int module_foreach(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -612,6 +619,7 @@  static int module_init(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -791,6 +799,7 @@  static int module_status(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1387,6 +1396,7 @@  static int module_sync(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1541,6 +1551,7 @@  static int module_deinit(int argc, const char **argv, const char *prefix)
 
 	ret = 0;
 cleanup:
+	module_list_release(&list);
 	clear_pathspec(&pathspec);
 	return ret;
 }
@@ -1904,6 +1915,12 @@  struct submodule_update_clone {
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { 0 }
 
+static void submodule_update_clone_release(struct submodule_update_clone *suc)
+{
+	free(suc->update_clone);
+	free(suc->failed_clones);
+}
+
 struct update_data {
 	const char *prefix;
 	const char *displaypath;
@@ -1942,6 +1959,11 @@  struct update_data {
 	.max_jobs = 1, \
 }
 
+static void update_data_release(struct update_data *ud)
+{
+	module_list_release(&ud->list);
+}
+
 static void next_submodule_warn_missing(struct submodule_update_clone *suc,
 		struct strbuf *out, const char *displaypath)
 {
@@ -2595,6 +2617,7 @@  static int update_submodules(struct update_data *update_data)
 	}
 
 cleanup:
+	submodule_update_clone_release(&suc);
 	string_list_clear(&update_data->references, 0);
 	return res;
 }
@@ -2694,6 +2717,7 @@  static int module_update(int argc, const char **argv, const char *prefix)
 
 		if (module_list_compute(argc, argv, opt.prefix,
 					&pathspec2, &list) < 0) {
+			module_list_release(&list);
 			ret = 1;
 			goto cleanup;
 		}
@@ -2710,10 +2734,12 @@  static int module_update(int argc, const char **argv, const char *prefix)
 			info.flags |= OPT_QUIET;
 
 		for_each_listed_submodule(&list, init_submodule_cb, &info);
+		module_list_release(&list);
 	}
 
 	ret = update_submodules(&opt);
 cleanup:
+	update_data_release(&opt);
 	list_objects_filter_release(&filter_options);
 	clear_pathspec(&pathspec);
 	clear_pathspec(&pathspec2);
@@ -2833,6 +2859,7 @@  static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 	ret = 0;
 cleanup:
 	clear_pathspec(&pathspec);
+	module_list_release(&list);
 	return ret;
 }
 
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49cedc..3a241f259de 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -2,6 +2,7 @@ 
 
 test_description='test case exclude pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a submodule' '