git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 3/6] log: fix a memory leak in "git show <revision>..."

From
Junio C Hamano <gitster@pobox.com>
Date
Aug 3, 2022, 17:54 UTC
Message-ID
<xmqqv8r99pry.fsf@gitster.g>
In-Reply-To
<patch-v3-3.6-83fc1835fe5-20220802T152925Z-avarab@gmail.com>
Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
> Fix a memory leak in code added in 5d7eeee2ac6 (git-show: grok blobs,
> trees and tags, too, 2006-12-14). As we iterate over a "<revision>..."
> command-line and encounter ad OBJ_COMMIT we want to use our "struct
"... encounter an OBJ_COMMIT, we want to ..."?
Show 19 quoted lines
> rev_info", but with a "pending" array of one element: the one commit
> we're showing in the loop.
>
> To do this 5d7eeee2ac6 saved away a pointer to rev.pending.objects and
> rev.pending.nr for its iteration. We'd then clobber those (and alloc)
> when we needed to show an OBJ_COMMIT.
>
> We'd therefore leak the "rev.pending" we started out with, and only
> free the new "rev.pending" in the "OBJ_COMMIT" case arm as
> prepare_revision_walk() would draw it down.
>
> Let's fix this memory leak. Now when we encounter an OBJ_COMMIT we
> save away the "rev.pending" before clearing it. We then add a single
> commit to it, which our indirect invocation of prepare_revision_walk()
> will remove. After that we restore the "rev.pending".
>
> Our "rev.pending" will then get free'd by the release_revisions()
> added in f6bfea0ad01 (revisions API users: use release_revisions() in
> builtin/log.c, 2022-04-13)

Hmph, this gives me a strange sense of deja-vu that I saw a better solution in a separate patch from you, strange because I do not see anything at the tip of 'seen' like what I thought you did elsewhere.

We do need to reuse "rev_info" we got from outside the loop so that we will have to consistently apply diffopt and other things we got in there from the configuration and the command line. But after we decide to go to "each object is shown individually" mode, the contents of the pending array (rather, what we got from the command line in cmdline member) is only relevant to enumerate which individual objects are shown in the loop, and should not even be visible to the code that handles each individual object, e.g. even we pass &rev to this code when we see a blob:

		switch (o->type) {
		case OBJ_BLOB:
			ret = show_blob_object(&o->oid, &rev, name);
			break;

there is no point in exposing the rev.pending to show_blob_object() at al. The same is true for codepaths used to show a tag or a tree. When showing a commit, we do not even want the codepath that shows a single-commit range to touch and destroy the original rev.pending; we instead want to give a single-element pending array.

So instead of keeping rev.pending when we enter the loop and saving it away and restoring it, it feels a lot cleaner to invent/use an interface to "clone" the settings in an existing rev_info by:

 * Grab rev.pending into a separate "struct object_array" that is a
   local variable in cmd_show() and clear rev.pending, immediately
   after we decide to go to "show individually" mode.
 * Iterate over the objects in that local variable.  For each object
   to be shown, prepare a rev_info, clone the setting from the
   original rev_info, put that single object to the pending member
   of the clone, use that cloned rev_info, and release the resources
   held by the cloned rev_info once we are done.
Show 19 quoted lines
> diff --git a/builtin/log.c b/builtin/log.c
> index 88a5e98875a..b4b1d974617 100644
> --- a/builtin/log.c
> +++ b/builtin/log.c
> @@ -743,11 +743,17 @@ int cmd_show(int argc, const char **argv, const char *prefix)
>  			rev.shown_one = 1;
>  			break;
>  		case OBJ_COMMIT:
> +		{
> +			struct object_array old;
> +
> +			memcpy(&old, &rev.pending, sizeof(old));
>  			rev.pending.nr = rev.pending.alloc = 0;
>  			rev.pending.objects = NULL;
>  			add_object_array(o, name, &rev.pending);
>  			ret = cmd_log_walk_no_free(&rev);
> +			memcpy(&rev.pending, &old, sizeof(rev.pending));
>  			break;
> +		}

The reason why I find this approach a bit disturbing is that we pretend to know for certain that pending is the only thing we need to protect across multiple calls to the log_walk(), but I suspect that such an overconfidence will come back and bite us. We are not re-initializing other states reachable from the rev_info (e.g. the diff_options struct has various members that records what happened in an invocation, like needed_rename_limit, found_follow, and found_changes, that would want to be reinitialized if we are starting a new and totally independent traversal after we are done one traversal).

But I do not mind at all to leave such a fundamental clean-up to a totally separate topic, and keep this patch only about "we are clobbering and leaking rev.pending, let's plug the leak". In fact, I would prefer it that way. So take all of the above as material for possible NEEDSWORK comment, food for further thought.

Previous: Jeff KingNext: Ævar Arnfjörð Bjarmason
Message 42 of 52 in “revisions API: fix more memory leaks”
  1. 0/6 revisions API: fix more memory leaksÆvar Arnfjörð Bjarmason, Jul 13, 2022
  2. 1/6 bisect.c: add missing "goto" for release_revisions()Ævar Arnfjörð Bjarmason, Jul 13, 2022
  3. 2/6 test-fast-rebase helper: use release_revisions() (again)Ævar Arnfjörð Bjarmason, Jul 13, 2022
  4. 3/6 log: make the intent of cmd_show()'s "rev.pending" juggling clearerÆvar Arnfjörð Bjarmason, Jul 13, 2022
  5. Jeff KingJul 18, 2022
  6. 4/6 log: fix common "rev.pending" memory leak in "git show"Ævar Arnfjörð Bjarmason, Jul 13, 2022
  7. Jeff KingJul 18, 2022
  8. Ævar Arnfjörð BjarmasonJul 18, 2022
  9. 5/6 bisect.c: partially fix bisect_rev_setup() memory leakÆvar Arnfjörð Bjarmason, Jul 13, 2022
  10. Jeff KingJul 18, 2022
  11. 6/6 revisions API: don't leak memory on argv elements that need free()-ingÆvar Arnfjörð Bjarmason, Jul 13, 2022
  12. Jeff KingJul 18, 2022
  13. Ævar Arnfjörð BjarmasonJul 18, 2022
  14. Jeff KingJul 18, 2022
  15. Ævar Arnfjörð BjarmasonJul 29, 2022
  16. Jeff KingJul 29, 2022
  17. Jeff KingJul 29, 2022
  18. Junio C HamanoJul 29, 2022
  19. Jeff KingJul 29, 2022
  20. Jeff KingJul 18, 2022
  21. 0/6 revisions API: fix more memory leaksÆvar Arnfjörð Bjarmason, Jul 29, 2022
  22. 1/6 bisect.c: add missing "goto" for release_revisions()Ævar Arnfjörð Bjarmason, Jul 29, 2022
  23. 2/6 test-fast-rebase helper: use release_revisions() (again)Ævar Arnfjörð Bjarmason, Jul 29, 2022
  24. Eric SunshineJul 30, 2022
  25. 3/6 log: make the intent of cmd_show()'s "rev.pending" juggling clearerÆvar Arnfjörð Bjarmason, Jul 29, 2022
  26. Junio C HamanoJul 29, 2022
  27. 4/6 log: fix common "rev.pending" memory leak in "git show"Ævar Arnfjörð Bjarmason, Jul 29, 2022
  28. Jeff KingJul 29, 2022
  29. Jeff KingJul 29, 2022
  30. Jeff KingJul 29, 2022
  31. 5/6 bisect.c: partially fix bisect_rev_setup() memory leakÆvar Arnfjörð Bjarmason, Jul 29, 2022
  32. 6/6 revisions API: don't leak memory on argv elements that need free()-ingÆvar Arnfjörð Bjarmason, Jul 29, 2022
  33. Jeff KingJul 29, 2022
  34. Jeff KingJul 29, 2022
  35. 0/6 revisions API: fix more memory leaksÆvar Arnfjörð Bjarmason, Aug 2, 2022
  36. 1/6 bisect.c: add missing "goto" for release_revisions()Ævar Arnfjörð Bjarmason, Aug 2, 2022
  37. Junio C HamanoAug 3, 2022
  38. 2/6 test-fast-rebase helper: use release_revisions() (again)Ævar Arnfjörð Bjarmason, Aug 2, 2022
  39. 3/6 log: fix a memory leak in "git show <revision>..."Ævar Arnfjörð Bjarmason, Aug 2, 2022
  40. Jeff KingAug 3, 2022
  41. Jeff KingAug 3, 2022
  42. Junio C HamanoAug 3, 2022
  43. 5/6 bisect.c: partially fix bisect_rev_setup() memory leakÆvar Arnfjörð Bjarmason, Aug 2, 2022
  44. 4/6 log: refactor "rev.pending" code in cmd_show()Ævar Arnfjörð Bjarmason, Aug 2, 2022
  45. Jeff KingAug 3, 2022
  46. Junio C HamanoAug 3, 2022
  47. Ævar Arnfjörð BjarmasonAug 4, 2022
  48. Junio C HamanoAug 4, 2022
  49. Ævar Arnfjörð BjarmasonAug 5, 2022
  50. 6/6 revisions API: don't leak memory on argv elements that need free()-ingÆvar Arnfjörð Bjarmason, Aug 2, 2022
  51. Junio C HamanoAug 3, 2022
  52. Jeff KingAug 3, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.