[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