Thread (16 messages) flat view 16 messages, 4 authors, 3d ago
WARM3d

[PATCH v3] builtin/history: unuse the commit buffer after use

From: Kaartic Sivaraam <hidden>
Date: 2026-09-10 15:00:32
Subsystem: the rest · Maintainer: Linus Torvalds

While running `git history reword` on a commit with `SANITIZE` flag set
to `address,leak`, we could observe a leak being reported (trace near
the end).

The root cause is as follows.

As part of rewording a commit, `commit_tree_ext` obtains the commit
message buffer from `repo_logmsg_reencode`. As we ask for no output
encoding, that function hands back the buffer from
`repo_get_commit_buffer` verbatim.

`repo_get_commit_buffer` returns the buffer cached in the commit slab
if there is one, and otherwise reads the object afresh via
`odb_read_object`. As the stacktrace below shows, we take the latter
path here. The buffer is uncached because the commit was parsed from
the commit-graph: such a parse is answered from the graph file alone,
so it never reads the object and never caches a buffer.

A buffer obtained this way is expected to be released with an
accompanying call to `repo_unuse_commit_buffer`, which takes care of
freeing it. This call is missing in the `commit_tree_ext` flow, thus
resulting in the leak.

Fix this by ensuring we call `repo_unuse_commit_buffer` on the
original_message buffer.

Using `repo_unuse_commit_buffer` is the correct way to release the
buffer since it frees only buffers the slab doesn't own. Plain free
would double-free on the cached path.

For those who are curious, the following is a minimal way to reproduce
the leak. Note the `git commit-graph --write` step, without which the
buffer comes from the commit slab and nothing leaks:

  $ git init scratch
  Initialized empty Git repository in /me/test-repos/scratch/.git/
  $ cd scratch/
  $ touch one && git add one && git commit -m "Commit one"
  [main (root-commit) 2182f9c] Commit one
   1 file changed, 0 insertions(+), 0 deletions(-)
   create mode 100644 one
  $ touch two && git add two && git commit -m "Commit two"
  [main 5550f33] Commit two
   1 file changed, 0 insertions(+), 0 deletions(-)
   create mode 100644 two
  $ git commit-graph write --reachable
  $ git history reword HEAD --dry-run
  update refs/heads/main eaded0872b14b3937605c77c0042429ca1e3bbe1
   fd19e3776c75b8da9555c7c616ce0df9db7c6641

This leak could also be triggered in our test suite if we run
t3451-history-reword.sh as follows:

  $ make SANITIZE=leak
  $ cd t
  $ GIT_TEST_COMMIT_GRAPH=1 ./t3451-history-reword.sh -v -i

=== Memory leak strack trace ===

==122337==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 263 byte(s) in 1 object(s) allocated from:
    #0 0x7002c14fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x5cdd008ec077 in do_xmalloc /me/git/wrapper.c:55
    #2 0x5cdd008ec185 in do_xmallocz /me/git/wrapper.c:89
    #3 0x5cdd008ec1fa in xmallocz /me/git/wrapper.c:97
    #4 0x5cdd005b99d8 in unpack_loose_rest /me/git/object-file.c:216
    #5 0x5cdd005e45f4 in read_object_info_from_path odb/source-loose.c:174
    #6 0x5cdd005e4ba0 in odb_source_loose_read_object_info odb/source-loose.c:235
    #7 0x5cdd005d9f83 in odb_source_read_object_info odb/source.h:413
    #8 0x5cdd005daaed in odb_source_files_read_object_info odb/source-files.c:93
    #9 0x5cdd005d1c8c in odb_source_read_object_info odb/source.h:413
    #10 0x5cdd005d5bdd in do_oid_object_info_extended /me/git/odb.c:592
    #11 0x5cdd005d7080 in odb_read_object_info_extended /me/git/odb.c:747
    #12 0x5cdd005d75d8 in odb_read_object /me/git/odb.c:793
    #13 0x5cdd003d9af7 in repo_get_commit_buffer /me/git/commit.c:399
    #14 0x5cdd006739ed in repo_logmsg_reencode /me/git/pretty.c:716
    #15 0x5cdd0012287a in commit_tree_ext builtin/history.c:134
    #16 0x5cdd00122f33 in commit_tree_with_edited_message builtin/history.c:190
    #17 0x5cdd00126e44 in cmd_history_reword builtin/history.c:748
    #18 0x5cdd0012b051 in cmd_history builtin/history.c:1209
    #19 0x5cdcfffb8faf in run_builtin /me/git/git.c:510
    #20 0x5cdcfffb9ac6 in handle_builtin /me/git/git.c:786
    #21 0x5cdcfffba358 in run_argv /me/git/git.c:869
    #22 0x5cdcfffbaea9 in cmd_main /me/git/git.c:990
    #23 0x5cdd0030f27f in main /me/git/common-main.c:9
    #24 0x7002c102a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #25 0x7002c102a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #26 0x5cdcfffb4134 in _start (/home/sivaraam/.local/bin/git+0x217134)
    (BuildId: 549c1036ab1f9f4fd55546e5bf31c7bd81b008fd)

SUMMARY: AddressSanitizer: 263 byte(s) leaked in 1 allocation(s).

Helped-by: Jeff King [off-list ref]
Signed-off-by: Kaartic Sivaraam <redacted>
---
Changes since v2:

- Tried to improve the commit message to make it more readable (hopefully).

 builtin/history.c | 1 +
 1 file changed, 1 insertion(+)
diff --git a/builtin/history.c b/builtin/history.c
index 091465a59e..0e9259b5d7 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -154,6 +154,7 @@ static int commit_tree_ext(struct repository *repo,
 	free_commit_extra_headers(original_extra_headers);
 	strbuf_release(&commit_message);
 	free(original_author);
+	repo_unuse_commit_buffer(repo, commit_with_message, original_message);
 	return ret;
 }
 
-- 
2.55.0.806.gb8242b093d
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help