pack-bitmap: prevent pattern leak on pseudo-merge re-assignment

When "bitmapPseudoMerge.*.pattern" appears more than once for the same
group, `pseudo_merge_config()` frees the old `regex_t *` pointer
but does not call `regfree()` on it first. This leaks whatever internal
state `regcomp()` allocated.

The final cleanup path in `pseudo_merge_group_release()` does call
`regfree()` before `free()`, so only the intermediate replacement is
affected.

Fix this by guarding the replacement with a NULL check and calling
`regfree()` before `free()` when the pointer is non-NULL.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
Taylor Blau 2026-05-11 20:47:12 -04:00 committed by Junio C Hamano
parent 84780db636
commit 5e6e8dc786
2 changed files with 33 additions and 1 deletions

View File

@ -150,7 +150,10 @@ static int pseudo_merge_config(const char *var, const char *value,
if (!strcmp(key, "pattern")) {
struct strbuf re = STRBUF_INIT;

free(group->pattern);
if (group->pattern) {
regfree(group->pattern);
free(group->pattern);
}
if (*value != '^')
strbuf_addch(&re, '^');
strbuf_addstr(&re, value);

View File

@ -662,4 +662,33 @@ test_expect_success 'sampleRate=0 does not cause division by zero' '
)
'

test_expect_success 'duplicate pseudo-merge pattern does not leak' '
test_when_finished "rm -fr pseudo-merge-dup-pattern" &&
git init pseudo-merge-dup-pattern &&
(
cd pseudo-merge-dup-pattern &&

test_commit_bulk 64 &&
tag_everything &&
git repack -ad &&

pack=$(ls .git/objects/pack/pack-*.pack) &&

# Set the same group'\''s pattern twice. The second
# assignment should cleanly release the compiled regex
# from the first without leaking.
git config bitmapPseudoMerge.test.pattern "refs/tags/" &&
git config --add bitmapPseudoMerge.test.pattern "refs/tags/" &&
git config bitmapPseudoMerge.test.maxMerges 1 &&
git config bitmapPseudoMerge.test.threshold now &&
git config bitmapPseudoMerge.test.stableThreshold never &&

git rev-parse HEAD~63 >in &&
test-tool bitmap write "$(basename $pack)" <in &&

test_pseudo_merges >merges &&
test_line_count = 1 merges
)
'

test_done