All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] history: close COMMIT_EDITMSG before launching the editor
@ 2026-06-25 18:33 Johannes Schindelin via GitGitGadget
  2026-06-25 20:06 ` Re* " Junio C Hamano
  0 siblings, 1 reply; 3+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2026-06-25 18:33 UTC (permalink / raw)
  To: git; +Cc: Patrick Steinhardt, Johannes Schindelin, Johannes Schindelin

From: Johannes Schindelin <johannes.schindelin@gmx.de>

The `git history reword` and `git history fixup` subcommands prepare the
commit message by writing it to COMMIT_EDITMSG and then opening that same
file a second time, in append mode, through `wt_status`'s `fp` field to
append the status information. That second handle is never closed before
`launch_editor()` runs, so the editor is started while git still holds
the file open.

Everywhere this leaks a file descriptor, but on Windows it is outright
broken: a process cannot replace a file that another process keeps open,
so an editor that rewrites COMMIT_EDITMSG by creating a fresh file in its
place fails. This surfaced while running Git for Windows' test suite with
BusyBox' `ash` as the POSIX shell: the fake editor's `cp message "$1"`
aborts with "cp: can't create '.../COMMIT_EDITMSG': File exists" (MSYS2's
coreutils `cp` hides the problem via its POSIX unlink emulation, BusyBox'
native `cp` does not), making t3451-history-reword and t3453-history-fixup
fail wholesale.

Close the handle once the status has been written, before handing the
file off to the editor.

Assisted-by: Opus 4.8
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
    history: close COMMIT_EDITMSG before launching the editor
    
    I noticed this problem while trying to whip MinGit-BusyBox into a better
    shape during the -rc phase. Technically, this is not a fix for a
    regression during the v2.55.0 period, but I figured it'd be better to
    send it now anyway than to forget about sending it after v2.55.0 is
    released.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2158%2Fdscho%2Ffix-fd-leak-in-history-reword-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2158/dscho/fix-fd-leak-in-history-reword-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2158

 builtin/history.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/builtin/history.c b/builtin/history.c
index 9526938085..4a5d9192f3 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -74,6 +74,14 @@ static int fill_commit_message(struct repository *repo,
 	wt_status_collect_free_buffers(&s);
 	string_list_clear_func(&s.change, change_data_free);
 
+	/*
+	 * Close the handle before launching the editor: on Windows an open
+	 * handle would prevent the editor from replacing the file (e.g.
+	 * BusyBox' `ash` cannot overwrite a file that another process keeps
+	 * open), and leaving it open leaks the descriptor everywhere else.
+	 */
+	fclose(s.fp);
+
 	strbuf_reset(out);
 	if (launch_editor(path, out, NULL)) {
 		fprintf(stderr, _("Aborting commit as launching the editor failed.\n"));

base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re* [PATCH] history: close COMMIT_EDITMSG before launching the editor
  2026-06-25 18:33 [PATCH] history: close COMMIT_EDITMSG before launching the editor Johannes Schindelin via GitGitGadget
@ 2026-06-25 20:06 ` Junio C Hamano
  2026-06-25 20:12   ` Junio C Hamano
  0 siblings, 1 reply; 3+ messages in thread
From: Junio C Hamano @ 2026-06-25 20:06 UTC (permalink / raw)
  To: Johannes Schindelin via GitGitGadget
  Cc: git, Patrick Steinhardt, Johannes Schindelin

"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> Close the handle once the status has been written, before handing the
> file off to the editor.
>
> Assisted-by: Opus 4.8

Do we even need this?

> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
>     history: close COMMIT_EDITMSG before launching the editor
>     
>     I noticed this problem while trying to whip MinGit-BusyBox into a better
>     shape during the -rc phase. Technically, this is not a fix for a
>     regression during the v2.55.0 period, but I figured it'd be better to
>     send it now anyway than to forget about sending it after v2.55.0 is
>     released.
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2158%2Fdscho%2Ffix-fd-leak-in-history-reword-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2158/dscho/fix-fd-leak-in-history-reword-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/2158
>
>  builtin/history.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
>
> diff --git a/builtin/history.c b/builtin/history.c
> index 9526938085..4a5d9192f3 100644
> --- a/builtin/history.c
> +++ b/builtin/history.c
> @@ -74,6 +74,14 @@ static int fill_commit_message(struct repository *repo,
>  	wt_status_collect_free_buffers(&s);
>  	string_list_clear_func(&s.change, change_data_free);
>  
> +	/*
> +	 * Close the handle before launching the editor: on Windows an open
> +	 * handle would prevent the editor from replacing the file (e.g.
> +	 * BusyBox' `ash` cannot overwrite a file that another process keeps
> +	 * open), and leaving it open leaks the descriptor everywhere else.
> +	 */
> +	fclose(s.fp);
> +
>  	strbuf_reset(out);
>  	if (launch_editor(path, out, NULL)) {
>  		fprintf(stderr, _("Aborting commit as launching the editor failed.\n"));

The function is extremely sloppy beyond words X-<.  Thanks for
taking the first step to clean it up.

 * It calls git_path_commit_editmsg() to obtain a constant pathname
   into "const char *path", but then the part that leads to this
   file stream leak does not even use that "path" constant.  It
   makes two independent calls to git_path_commit_editmsg()!

 * The function first calls write_file_buf() to the file, which is a
   convenience function when you have something you need to write
   upfront and just want to write it and be done with it.

 * And then the unclosed file stream you just fixed.

What is surprising is that all of this was created in a single
commit.  I suspected that this part that does fopen() to leak the
file stream was a later addition than the initial write_file_buf(),
which should have been critiqued with "once you want to do your
custom writing that is more than "I have this block of memory, write
it into file", you should rewrite write_file_buf() call and roll it
into your own custom writing", but that is not the case.

So taking the opportunity to clean things up, how about doing it
this way intead?

----- >8 ---------- >8 ---------- >8 -----

Subject: [PATCH] history: streamline message preparation and plug file stream leak

An early part of fill_commit_mmessage() function uses write_file_buf()
to write out what was prepared in a strbuf, which is primarily meant
for use by callers that have their own message prepared fully and
called as the last thing to flush it to the destination file.

However, the function then opens a file stream in append mode to
further write into it.  It may have been understandable if this was
a later addition, but it seems it came from a single commit,
d205234c (builtin/history: implement "reword" subcommand,
2026-01-13), which is somewhat puzzling, but anyway...

Just open the file stream upfront for writing, write the message
the function has in the strbuf, and then keep writing whatever it
wants to write to the same open file stream.

And do not forget to close the stream.  We are about to pass the
resulting file to an external editor, and on some systems, notably
Windows, you are not supposed to keep a file open while expecting
another program to access it.

Diagnosed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/history.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/builtin/history.c b/builtin/history.c
index 8dcb9a6046..a882ad82e5 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -41,11 +41,6 @@ static int fill_commit_message(struct repository *repo,
 		  " empty message aborts the commit.\n");
 	struct wt_status s;
 
-	strbuf_addstr(out, default_message);
-	strbuf_addch(out, '\n');
-	strbuf_commented_addf(out, comment_line_str, hint, action, comment_line_str);
-	write_file_buf(path, out->buf, out->len);
-
 	wt_status_prepare(repo, &s);
 	FREE_AND_NULL(s.branch);
 	s.ahead_behind_flags = AHEAD_BEHIND_QUICK;
@@ -57,14 +52,20 @@ static int fill_commit_message(struct repository *repo,
 	s.whence = FROM_COMMIT;
 	s.committable = 1;
 
-	s.fp = fopen(git_path_commit_editmsg(), "a");
+	s.fp = fopen(path, "w");
 	if (!s.fp)
-		return error_errno(_("could not open '%s'"), git_path_commit_editmsg());
+		return error_errno(_("could not open '%s'"), path);
+
+	strbuf_addstr(out, default_message);
+	strbuf_addch(out, '\n');
+	strbuf_commented_addf(out, comment_line_str, hint, action, comment_line_str);
+	fwrite(out.buf, 1, out.len, s.fp);
 
 	wt_status_collect_changes_trees(&s, old_tree, new_tree);
 	wt_status_print(&s);
 	wt_status_collect_free_buffers(&s);
 	string_list_clear_func(&s.change, change_data_free);
+	fclose(s.fp);
 
 	strbuf_reset(out);
 	if (launch_editor(path, out, NULL)) {
-- 
2.55.0-rc2-165-g3249676ba5


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: Re* [PATCH] history: close COMMIT_EDITMSG before launching the editor
  2026-06-25 20:06 ` Re* " Junio C Hamano
@ 2026-06-25 20:12   ` Junio C Hamano
  0 siblings, 0 replies; 3+ messages in thread
From: Junio C Hamano @ 2026-06-25 20:12 UTC (permalink / raw)
  To: Johannes Schindelin via GitGitGadget
  Cc: git, Patrick Steinhardt, Johannes Schindelin

Junio C Hamano <gitster@pobox.com> writes:

Of course, this should be ...

> +	fwrite(out.buf, 1, out.len, s.fp);

...

	fwrite(out->buf, 1, out->len, s.fp);


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-06-25 20:12 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-25 18:33 [PATCH] history: close COMMIT_EDITMSG before launching the editor Johannes Schindelin via GitGitGadget
2026-06-25 20:06 ` Re* " Junio C Hamano
2026-06-25 20:12   ` Junio C Hamano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.