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

Re: [PATCH 13/17] rebase: don't leak on "--abort"

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Nov 5, 2022, 12:01 UTC
Message-ID
<221105.86eduhy3l8.gmgdl@evledraar.gmail.com>
In-Reply-To
<f9df4dfc-6024-deb0-5277-bab96c81a875@dunelm.org.uk>
On Fri, Nov 04 2022, Phillip Wood wrote:
Show 29 quoted lines
> On 03/11/2022 17:06, Ævar Arnfjörð Bjarmason wrote:
>> Fix a leak in the recent 6159e7add49 (rebase --abort: improve reflog
>> message, 2022-10-12). Before that commit we'd strbuf_release() the
>> reflog message we were formatting, but when that code was refactored
>> to use "ropts.head_msg" the strbuf_release() was omitted.
>> Ideally the three users of "ropts" in cmd_rebase() should use
>> different "ropts" variables, in practice they're completely separate,
>> as this and the other user in the "switch" statement will "goto
>> cleanup", which won't touch "ropts".
>> The third caller after the "switch" is then unreachable if we take
>> these two branches, so all of them are getting a "{ 0 }" init'd
>> "ropts".
>> So it's OK that we're leaving a stale pointer in "ropts.head_msg",
>> cleaning it up was our responsibility, and it won't be used again.
>> [...]
>> --- a/builtin/rebase.c
>> +++ b/builtin/rebase.c
>> @@ -1320,6 +1320,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>>   		if (reset_head(the_repository, &ropts) < 0)
>>   			die(_("could not move back to %s"),
>>   			    oid_to_hex(&options.orig_head->object.oid));
>> +		strbuf_release(&head_msg);
>>   		remove_branch_state(the_repository, 0);
>>   		ret = finish_rebase(&options);
>>   		goto cleanup;
>
> This looks fine. One could argue that the leak is not a "real" leak as
> we're about to exit but the omission of strbuf_release() was 
> unintentional and fix is nice and simple.

What I've been targeting in this and past leak fix topics is roughly what valgrind calls[1] "definitely lost" and "indirectly lost", and which -fsanitize=leak calls "that thing I die on whan I run into it" :)

So, you and I know we're "about to exit" as we're in cmd_rebase() and about to return to common-main.c etc, but it's harder to convince a compiler of that.

Maybe I'm just touchy, sorry. It just feels like every time I submit a leak fix topic there's some rehash of the "why do this at all?" or "why this one?" discussion.

To summarize (maybe too much for just this reply, but I hope to link back to this in the future):

 * Historically we've said "that's just a built-in, let the OS take care
   of it!"
 * I agree with that in principle. It's pretty pointless to free()
   things in cmd_*() in the abstract. We're "about to exit", even if
   valgrind etc. call it "definitely lost"[1].
 * But, it would be nice to have git's APIs not leak, so we could call
   them in loops, replace some of our own sub-process invocations
   etc[2].
 * If we had 100% test coverage for those libraries we could just test
   that, and get those leak-free, and ignore the built-ins.
 * We don't have that, and probably never will. E.g. "git log" and
   friends are *the* things that stress revision.[ch]. So to assert that
   the API is leak-free we really need to assert that its users
   are. Ditto for pretty much any other API we carry.
 * Still, we could just say "if something's alloc'd in cmd_*() don't
   care about it". Both LSAN and valgrind support "suppressions" to
   ignore certain functions, patterns etc.
 * But that doesn't really work either, as e.g. "struct rev_info" would
   seem to come from such a cmd_*() as far as ownership goes...
 * ..."but you could have an even more clever suppressions mechanism,
   and only ignore those things that are malloc'd by cmd_*(). But not
   e.g. a malloc in revision.c in a struct member in a struct that's on
   the stack in a cmd_*()", one might say.
   Yeah, you could do that, e.g. in this case we'll call strbuf_addf()
   which makes a beeline to malloc(). Sufficiently clever
   post-processing of leak stacktraces could probably make the
   distinction.
   But then you run into the problem of e.g. how the the log family and
   others use "mailmap". I.e. in that case (and many others) the cmd_*()
   will malloc(), but "hand it off" to the API, whose job we know it is
   to free() it.
   But we only know because of knowledge about the API, an automated
   system would care, still, it's not unworkable. We could go through
   those, list the exceptions etc. etc.

That's a lot of context, but I think brings us up-to-date on this one-line change.

So, could we do things differently here? Definitely. And if the codebase was in a state where e.g. builtin/*.c had ~1500 UNLEAK() already I'd probably try to extend that, rather than fighting that uphill battle.

But that's not the case, we have around ~30 of them, fewer after this topic. By far the majority of builtin/* is caring about directly recahable leaks already.

E.g. just above this context in another "case" arm we're doing the exact "variable on stack, use it, then release()", just with a "struct string_list".

So I think this is the right direction to go in, both in this patch & in general.

1. https://valgrind.org/docs/manual/mc-manual.html#mc-manual.leaks
2. Even the assumption that cmd_*() is a one-off is shaky, e.g. for
   cases where we shell out to "git commit" we're close now to being
   able to just call cmd_commit(), but let's leave that aside for now).
   So, we're pretty close to cmd_*() being a funny API that happens to
   take strvec arguments, which is going to be the shortest path to
   eliminating sub-process invocation in many cases. I.e. as opposed to
   refactoring the cmd_*() into a "real" library, that takes a struct
   with options, doesn't call parse_options() etc.
Previous: Phillip WoodNext: Ævar Arnfjörð Bjarmason
Message 21 of 45 in “leak fixes: use existing constructors & other trivia”
  1. 00/17 leak fixes: use existing constructors & other triviaÆvar Arnfjörð Bjarmason, Nov 3, 2022
  2. 02/17 {reset,merge}: call discard_index() before returningÆvar Arnfjörð Bjarmason, Nov 3, 2022
  3. 01/17 tests: mark tests as passing with SANITIZE=leakÆvar Arnfjörð Bjarmason, Nov 3, 2022
  4. 04/17 read-cache.c: clear and free "sparse_checkout_patterns"Ævar Arnfjörð Bjarmason, Nov 3, 2022
  5. 05/17 dir.c: free "ident" and "exclude_per_dir" in "struct untracked_cache"Ævar Arnfjörð Bjarmason, Nov 3, 2022
  6. 06/17 built-ins & libs & helpers: add/move destructors, fix leaksÆvar Arnfjörð Bjarmason, Nov 3, 2022
  7. 07/17 unpack-file: fix ancient leak in create_temp_file()Ævar Arnfjörð Bjarmason, Nov 3, 2022
  8. 09/17 ls-files: fix a --with-tree memory leakÆvar Arnfjörð Bjarmason, Nov 3, 2022
  9. 10/17 sequencer.c: fix "opts->strategy" leak in read_strategy_opts()Ævar Arnfjörð Bjarmason, Nov 3, 2022
  10. Phillip WoodNov 4, 2022
  11. Taylor BlauNov 4, 2022
  12. Ævar Arnfjörð BjarmasonNov 5, 2022
  13. Phillip WoodNov 8, 2022
  14. Ævar Arnfjörð BjarmasonNov 8, 2022
  15. 03/17 commit: discard partial cache before (re-)reading itÆvar Arnfjörð Bjarmason, Nov 3, 2022
  16. 08/17 revision API: call graph_clear() in release_revisions()Ævar Arnfjörð Bjarmason, Nov 3, 2022
  17. 12/17 sequencer.c: fix a pick_commits() leakÆvar Arnfjörð Bjarmason, Nov 3, 2022
  18. 11/17 connected.c: free the "struct packed_git"Ævar Arnfjörð Bjarmason, Nov 3, 2022
  19. 13/17 rebase: don't leak on "--abort"Ævar Arnfjörð Bjarmason, Nov 3, 2022
  20. Phillip WoodNov 4, 2022
  21. Ævar Arnfjörð BjarmasonNov 5, 2022
  22. 16/17 revert: fix parse_options_concat() leakÆvar Arnfjörð Bjarmason, Nov 3, 2022
  23. 14/17 sequencer.c: fix sequencer_continue() leakÆvar Arnfjörð Bjarmason, Nov 3, 2022
  24. 15/17 cherry-pick: free "struct replay_opts" membersÆvar Arnfjörð Bjarmason, Nov 3, 2022
  25. 17/17 built-ins: use free() not UNLEAK() if trivial, rm dead codeÆvar Arnfjörð Bjarmason, Nov 3, 2022
  26. Phillip WoodNov 4, 2022
  27. Ævar Arnfjörð BjarmasonNov 5, 2022
  28. Phillip WoodNov 7, 2022
  29. 00/15 leak fixes: use existing constructors & other triviaÆvar Arnfjörð Bjarmason, Nov 8, 2022
  30. 01/15 tests: mark tests as passing with SANITIZE=leakÆvar Arnfjörð Bjarmason, Nov 8, 2022
  31. 02/15 {reset,merge}: call discard_index() before returningÆvar Arnfjörð Bjarmason, Nov 8, 2022
  32. 04/15 read-cache.c: clear and free "sparse_checkout_patterns"Ævar Arnfjörð Bjarmason, Nov 8, 2022
  33. 03/15 commit: discard partial cache before (re-)reading itÆvar Arnfjörð Bjarmason, Nov 8, 2022
  34. 05/15 dir.c: free "ident" and "exclude_per_dir" in "struct untracked_cache"Ævar Arnfjörð Bjarmason, Nov 8, 2022
  35. 06/15 built-ins & libs & helpers: add/move destructors, fix leaksÆvar Arnfjörð Bjarmason, Nov 8, 2022
  36. 08/15 revision API: call graph_clear() in release_revisions()Ævar Arnfjörð Bjarmason, Nov 8, 2022
  37. 07/15 unpack-file: fix ancient leak in create_temp_file()Ævar Arnfjörð Bjarmason, Nov 8, 2022
  38. 09/15 ls-files: fix a --with-tree memory leakÆvar Arnfjörð Bjarmason, Nov 8, 2022
  39. 10/15 sequencer.c: fix "opts->strategy" leak in read_strategy_opts()Ævar Arnfjörð Bjarmason, Nov 8, 2022
  40. 11/15 connected.c: free the "struct packed_git"Ævar Arnfjörð Bjarmason, Nov 8, 2022
  41. 13/15 cherry-pick: free "struct replay_opts" membersÆvar Arnfjörð Bjarmason, Nov 8, 2022
  42. 12/15 rebase: don't leak on "--abort"Ævar Arnfjörð Bjarmason, Nov 8, 2022
  43. 15/15 built-ins: use free() not UNLEAK() if trivial, rm dead codeÆvar Arnfjörð Bjarmason, Nov 8, 2022
  44. 14/15 revert: fix parse_options_concat() leakÆvar Arnfjörð Bjarmason, Nov 8, 2022
  45. Taylor BlauNov 8, 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.