sequencer: fix leaking string buffer in `commit_staged_changes()`

We're leaking the `rev` string buffer in various call paths. Refactor
the function to have a common exit path so that we can release its
memory reliably.

This fixes a subset of tests failing with the memory sanitizer in t3404.
But as there are more failures, we cannot yet mark the whole test suite
as passing.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Patrick Steinhardt 2024-06-11 11:20:47 +02:00 committed by Junio C Hamano
parent 63c9bd372e
commit 1e5c1601f9
1 changed files with 73 additions and 38 deletions

View File

@ -5146,33 +5146,47 @@ static int commit_staged_changes(struct repository *r,
struct replay_ctx *ctx = opts->ctx; struct replay_ctx *ctx = opts->ctx;
unsigned int flags = ALLOW_EMPTY | EDIT_MSG; unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
unsigned int final_fixup = 0, is_clean; unsigned int final_fixup = 0, is_clean;
struct strbuf rev = STRBUF_INIT;
int ret;


if (has_unstaged_changes(r, 1)) if (has_unstaged_changes(r, 1)) {
return error(_("cannot rebase: You have unstaged changes.")); ret = error(_("cannot rebase: You have unstaged changes."));
goto out;
}


is_clean = !has_uncommitted_changes(r, 0); is_clean = !has_uncommitted_changes(r, 0);


if (!is_clean && !file_exists(rebase_path_message())) { if (!is_clean && !file_exists(rebase_path_message())) {
const char *gpg_opt = gpg_sign_opt_quoted(opts); const char *gpg_opt = gpg_sign_opt_quoted(opts);

ret = error(_(staged_changes_advice), gpg_opt, gpg_opt);
return error(_(staged_changes_advice), gpg_opt, gpg_opt); goto out;
} }

if (file_exists(rebase_path_amend())) { if (file_exists(rebase_path_amend())) {
struct strbuf rev = STRBUF_INIT;
struct object_id head, to_amend; struct object_id head, to_amend;


if (repo_get_oid(r, "HEAD", &head)) if (repo_get_oid(r, "HEAD", &head)) {
return error(_("cannot amend non-existing commit")); ret = error(_("cannot amend non-existing commit"));
if (!read_oneliner(&rev, rebase_path_amend(), 0)) goto out;
return error(_("invalid file: '%s'"), rebase_path_amend()); }
if (get_oid_hex(rev.buf, &to_amend))
return error(_("invalid contents: '%s'"), if (!read_oneliner(&rev, rebase_path_amend(), 0)) {
ret = error(_("invalid file: '%s'"), rebase_path_amend());
goto out;
}

if (get_oid_hex(rev.buf, &to_amend)) {
ret = error(_("invalid contents: '%s'"),
rebase_path_amend()); rebase_path_amend());
if (!is_clean && !oideq(&head, &to_amend)) goto out;
return error(_("\nYou have uncommitted changes in your " }
if (!is_clean && !oideq(&head, &to_amend)) {
ret = error(_("\nYou have uncommitted changes in your "
"working tree. Please, commit them\n" "working tree. Please, commit them\n"
"first and then run 'git rebase " "first and then run 'git rebase "
"--continue' again.")); "--continue' again."));
goto out;
}
/* /*
* When skipping a failed fixup/squash, we need to edit the * When skipping a failed fixup/squash, we need to edit the
* commit message, the current fixup list and count, and if it * commit message, the current fixup list and count, and if it
@ -5204,9 +5218,11 @@ static int commit_staged_changes(struct repository *r,
len--; len--;
strbuf_setlen(&ctx->current_fixups, len); strbuf_setlen(&ctx->current_fixups, len);
if (write_message(p, len, rebase_path_current_fixups(), if (write_message(p, len, rebase_path_current_fixups(),
0) < 0) 0) < 0) {
return error(_("could not write file: '%s'"), ret = error(_("could not write file: '%s'"),
rebase_path_current_fixups()); rebase_path_current_fixups());
goto out;
}


/* /*
* If a fixup/squash in a fixup/squash chain failed, the * If a fixup/squash in a fixup/squash chain failed, the
@ -5236,35 +5252,38 @@ static int commit_staged_changes(struct repository *r,
* We need to update the squash message to skip * We need to update the squash message to skip
* the latest commit message. * the latest commit message.
*/ */
int res = 0;
struct commit *commit; struct commit *commit;
const char *msg; const char *msg;
const char *path = rebase_path_squash_msg(); const char *path = rebase_path_squash_msg();
const char *encoding = get_commit_output_encoding(); const char *encoding = get_commit_output_encoding();


if (parse_head(r, &commit)) if (parse_head(r, &commit)) {
return error(_("could not parse HEAD")); ret = error(_("could not parse HEAD"));
goto out;
}


p = repo_logmsg_reencode(r, commit, NULL, encoding); p = repo_logmsg_reencode(r, commit, NULL, encoding);
if (!p) { if (!p) {
res = error(_("could not parse commit %s"), ret = error(_("could not parse commit %s"),
oid_to_hex(&commit->object.oid)); oid_to_hex(&commit->object.oid));
goto unuse_commit_buffer; goto unuse_commit_buffer;
} }
find_commit_subject(p, &msg); find_commit_subject(p, &msg);
if (write_message(msg, strlen(msg), path, 0)) { if (write_message(msg, strlen(msg), path, 0)) {
res = error(_("could not write file: " ret = error(_("could not write file: "
"'%s'"), path); "'%s'"), path);
goto unuse_commit_buffer; goto unuse_commit_buffer;
} }

ret = 0;

unuse_commit_buffer: unuse_commit_buffer:
repo_unuse_commit_buffer(r, commit, p); repo_unuse_commit_buffer(r, commit, p);
if (res) if (ret)
return res; goto out;
} }
} }


strbuf_release(&rev);
flags |= AMEND_MSG; flags |= AMEND_MSG;
} }


@ -5272,18 +5291,29 @@ static int commit_staged_changes(struct repository *r,
if (refs_ref_exists(get_main_ref_store(r), if (refs_ref_exists(get_main_ref_store(r),
"CHERRY_PICK_HEAD") && "CHERRY_PICK_HEAD") &&
refs_delete_ref(get_main_ref_store(r), "", refs_delete_ref(get_main_ref_store(r), "",
"CHERRY_PICK_HEAD", NULL, REF_NO_DEREF)) "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF)) {
return error(_("could not remove CHERRY_PICK_HEAD")); ret = error(_("could not remove CHERRY_PICK_HEAD"));
if (unlink(git_path_merge_msg(r)) && errno != ENOENT) goto out;
return error_errno(_("could not remove '%s'"), }

if (unlink(git_path_merge_msg(r)) && errno != ENOENT) {
ret = error_errno(_("could not remove '%s'"),
git_path_merge_msg(r)); git_path_merge_msg(r));
if (!final_fixup) goto out;
return 0; }

if (!final_fixup) {
ret = 0;
goto out;
}
} }


if (run_git_commit(final_fixup ? NULL : rebase_path_message(), if (run_git_commit(final_fixup ? NULL : rebase_path_message(),
opts, flags)) opts, flags)) {
return error(_("could not commit staged changes.")); ret = error(_("could not commit staged changes."));
goto out;
}

unlink(rebase_path_amend()); unlink(rebase_path_amend());
unlink(git_path_merge_head(r)); unlink(git_path_merge_head(r));
refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE", refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
@ -5301,7 +5331,12 @@ static int commit_staged_changes(struct repository *r,
strbuf_reset(&ctx->current_fixups); strbuf_reset(&ctx->current_fixups);
ctx->current_fixup_count = 0; ctx->current_fixup_count = 0;
} }
return 0;
ret = 0;

out:
strbuf_release(&rev);
return ret;
} }


int sequencer_continue(struct repository *r, struct replay_opts *opts) int sequencer_continue(struct repository *r, struct replay_opts *opts)