{"thread":{"id":"58271","subject":"[PATCH] hook API: don't segfault on strbuf_addf() to NULL \"out\"","startedAt":"2022-08-05T14:15:49Z","lastAt":"2022-08-05T21:09:20Z","messageCount":4,"participants":["Ævar Arnfjörð Bjarmason","Đoàn Trần Công Danh","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"460707","messageId":"patch-1.1-2450e3e65cf-20220805T141402Z-avarab@gmail.com","threadId":"58271","inReplyTo":"0015309-00f1-9b44-023c-001ee3f242e4@0upti.me","subject":"[PATCH] hook API: don't segfault on strbuf_addf() to NULL \"out\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-05T14:15:33Z","receivedAt":"2022-08-05T14:15:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a logic error in a082345372e (hook API: fix v2.36.0 regression:\nhooks should be connected to a TTY, 2022-06-07). When it started using\nthe \"ungroup\" API added in fd3aaf53f71 (run-command: add an \"ungroup\"\noption to run_process_parallel(), 2022-06-07) it should have made the\nsame sort of change that fd3aaf53f71 itself made in\n\"t/helper/test-run-command.c\".\n\nThe correct way to emit this \"Couldn't start\" output with \"ungroup\"\nwould be:\n\n\tfprintf(stderr, _(\"Couldn't start hook '%s'\\n\"), hook_path);\n\nBut we should instead remove the emitting of this output. As the added\ntest shows we already emit output when we can't run the child. The\n\"cannot run\" output here is emitted by run-command.c's\nchild_err_spew().\n\nSo the addition of the \"Couldn't start hook\" output here in\n96e7225b310 (hook: add 'run' subcommand, 2021-12-22) was always\nredundant. For the pre-commit hook we'll now emit exactly the same\noutput as we did before f443246b9f2 (commit: convert\n{pre-commit,prepare-commit-msg} hook to hook.h, 2021-12-22) (and\nlikewise for others).\n\nWe could at this point add this to the pick_next_hook() callbacks in\nhook.c:\n\n\tassert(!out);\n\tassert(!*pp_task_cb);\n\nAnd this to notify_start_failure() and notify_hook_finished() (in the\nlatter case the parameter is called \"pp_task_cp\"):\n\n\tassert(!out);\n\tassert(!pp_task_cb);\n\nBut let's leave any such instrumentation for some eventual cleanup of\nthe \"ungroup\" API.\n\nReported-by: Ilya K <me@0upti.me>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nAs suspected I needed to bounce this off the CI once to due to the\nOS-specific messaging around failed hooks, it passes now:\nhttps://github.com/avar/git/actions/runs/2802782493\n\n hook.c          |  7 -------\n t/t1800-hook.sh | 26 ++++++++++++++++++++++++++\n 2 files changed, 26 insertions(+), 7 deletions(-)\n\ndiff --git a/hook.c b/hook.c\nindex d113ee7faae..a493939a4fc 100644\n--- a/hook.c\n+++ b/hook.c\n@@ -62,9 +62,6 @@ static int pick_next_hook(struct child_process *cp,\n \tstrvec_push(&cp->args, hook_path);\n \tstrvec_pushv(&cp->args, hook_cb->options->args.v);\n \n-\t/* Provide context for errors if necessary */\n-\t*pp_task_cb = (char *)hook_path;\n-\n \t/*\n \t * This pick_next_hook() will be called again, we're only\n \t * running one hook, so indicate that no more work will be\n@@ -80,13 +77,9 @@ static int notify_start_failure(struct strbuf *out,\n \t\t\t\tvoid *pp_task_cp)\n {\n \tstruct hook_cb_data *hook_cb = pp_cb;\n-\tconst char *hook_path = pp_task_cp;\n \n \thook_cb->rc |= 1;\n \n-\tstrbuf_addf(out, _(\"Couldn't start hook '%s'\\n\"),\n-\t\t    hook_path);\n-\n \treturn 1;\n }\n \ndiff --git a/t/t1800-hook.sh b/t/t1800-hook.sh\nindex 210f4298872..64096adac7e 100755\n--- a/t/t1800-hook.sh\n+++ b/t/t1800-hook.sh\n@@ -151,4 +151,30 @@ test_expect_success TTY 'git commit: stdout and stderr are connected to a TTY' '\n \ttest_hook_tty commit -m\"B.new\"\n '\n \n+test_expect_success 'git hook run a hook with a bad shebang' '\n+\ttest_when_finished \"rm -rf bad-hooks\" &&\n+\tmkdir bad-hooks &&\n+\twrite_script bad-hooks/test-hook \"/bad/path/no/spaces\" </dev/null &&\n+\n+\t# TODO: We should emit the same (or at least a more similar)\n+\t# error on Windows and !Windows. See the OS-specific code in\n+\t# start_command()\n+\tif test_have_prereq !WINDOWS\n+\tthen\n+\t\tcat >expect <<-\\EOF\n+\t\tfatal: cannot run bad-hooks/test-hook: ...\n+\t\tEOF\n+\telse\n+\t\tcat >expect <<-\\EOF\n+\t\terror: cannot spawn bad-hooks/test-hook: ...\n+\t\tEOF\n+\tfi &&\n+\ttest_expect_code 1 git \\\n+\t\t-c core.hooksPath=bad-hooks \\\n+\t\thook run test-hook >out 2>err &&\n+\ttest_must_be_empty out &&\n+\tsed -e \"s/test-hook: .*/test-hook: .../\" <err >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.37.1.1283.g2c56c4202c5\n\n"},{"id":"460716","messageId":"Yu08MCCoGwTxeM+g@danh.dev","threadId":"58271","inReplyTo":"patch-1.1-2450e3e65cf-20220805T141402Z-avarab@gmail.com","subject":"Re: [PATCH] hook API: don't segfault on strbuf_addf() to NULL \"out\"","fromName":"Đoàn Trần Công Danh","fromEmail":"congdanhqx@gmail.com","sentAt":"2022-08-05T15:50:08Z","receivedAt":"2022-08-05T15:53:04Z","isPatch":true,"sender":{"key":"congdanhqx@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42673067?v=4"},"body":"On 2022-08-05 16:15:33+0200, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> Fix a logic error in a082345372e (hook API: fix v2.36.0 regression:\n> hooks should be connected to a TTY, 2022-06-07). When it started using\n> the \"ungroup\" API added in fd3aaf53f71 (run-command: add an \"ungroup\"\n> option to run_process_parallel(), 2022-06-07) it should have made the\n> same sort of change that fd3aaf53f71 itself made in\n> \"t/helper/test-run-command.c\".\n> \n> The correct way to emit this \"Couldn't start\" output with \"ungroup\"\n> would be:\n> \n> \tfprintf(stderr, _(\"Couldn't start hook '%s'\\n\"), hook_path);\n> \n> But we should instead remove the emitting of this output. As the added\n> test shows we already emit output when we can't run the child. The\n> \"cannot run\" output here is emitted by run-command.c's\n> child_err_spew().\n> \n> So the addition of the \"Couldn't start hook\" output here in\n> 96e7225b310 (hook: add 'run' subcommand, 2021-12-22) was always\n> redundant. For the pre-commit hook we'll now emit exactly the same\n> output as we did before f443246b9f2 (commit: convert\n> {pre-commit,prepare-commit-msg} hook to hook.h, 2021-12-22) (and\n> likewise for others).\n> \n> We could at this point add this to the pick_next_hook() callbacks in\n> hook.c:\n> \n> \tassert(!out);\n> \tassert(!*pp_task_cb);\n> \n> And this to notify_start_failure() and notify_hook_finished() (in the\n> latter case the parameter is called \"pp_task_cp\"):\n> \n> \tassert(!out);\n> \tassert(!pp_task_cb);\n> \n> But let's leave any such instrumentation for some eventual cleanup of\n> the \"ungroup\" API.\n> \n> Reported-by: Ilya K <me@0upti.me>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> \n> As suspected I needed to bounce this off the CI once to due to the\n> OS-specific messaging around failed hooks, it passes now:\n> https://github.com/avar/git/actions/runs/2802782493\n> \n>  hook.c          |  7 -------\n>  t/t1800-hook.sh | 26 ++++++++++++++++++++++++++\n>  2 files changed, 26 insertions(+), 7 deletions(-)\n> \n> diff --git a/hook.c b/hook.c\n> index d113ee7faae..a493939a4fc 100644\n> --- a/hook.c\n> +++ b/hook.c\n> @@ -62,9 +62,6 @@ static int pick_next_hook(struct child_process *cp,\n>  \tstrvec_push(&cp->args, hook_path);\n>  \tstrvec_pushv(&cp->args, hook_cb->options->args.v);\n>  \n> -\t/* Provide context for errors if necessary */\n> -\t*pp_task_cb = (char *)hook_path;\n> -\n>  \t/*\n>  \t * This pick_next_hook() will be called again, we're only\n>  \t * running one hook, so indicate that no more work will be\n> @@ -80,13 +77,9 @@ static int notify_start_failure(struct strbuf *out,\n>  \t\t\t\tvoid *pp_task_cp)\n>  {\n>  \tstruct hook_cb_data *hook_cb = pp_cb;\n> -\tconst char *hook_path = pp_task_cp;\n>  \n>  \thook_cb->rc |= 1;\n>  \n> -\tstrbuf_addf(out, _(\"Couldn't start hook '%s'\\n\"),\n> -\t\t    hook_path);\n> -\n>  \treturn 1;\n>  }\n>  \n> diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh\n> index 210f4298872..64096adac7e 100755\n> --- a/t/t1800-hook.sh\n> +++ b/t/t1800-hook.sh\n> @@ -151,4 +151,30 @@ test_expect_success TTY 'git commit: stdout and stderr are connected to a TTY' '\n>  \ttest_hook_tty commit -m\"B.new\"\n>  '\n>  \n> +test_expect_success 'git hook run a hook with a bad shebang' '\n> +\ttest_when_finished \"rm -rf bad-hooks\" &&\n> +\tmkdir bad-hooks &&\n> +\twrite_script bad-hooks/test-hook \"/bad/path/no/spaces\" </dev/null &&\n> +\n> +\t# TODO: We should emit the same (or at least a more similar)\n> +\t# error on Windows and !Windows. See the OS-specific code in\n> +\t# start_command()\n> +\tif test_have_prereq !WINDOWS\n> +\tthen\n> +\t\tcat >expect <<-\\EOF\n> +\t\tfatal: cannot run bad-hooks/test-hook: ...\n> +\t\tEOF\n> +\telse\n> +\t\tcat >expect <<-\\EOF\n> +\t\terror: cannot spawn bad-hooks/test-hook: ...\n> +\t\tEOF\n> +\tfi &&\n> +\ttest_expect_code 1 git \\\n> +\t\t-c core.hooksPath=bad-hooks \\\n> +\t\thook run test-hook >out 2>err &&\n> +\ttest_must_be_empty out &&\n> +\tsed -e \"s/test-hook: .*/test-hook: .../\" <err >actual &&\n\nIf we're using \"sed\" here, can we also s/cannot run/cannot spawn/\nin order to have the same expectation?\n\nOtherwise, the fix looks sane to me (obviously, since I also suggest\nremoving the line entirely).\n\nReviewed-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n\n> +\ttest_cmp expect actual\n> +'\n> +\n>  test_done\n> -- \n> 2.37.1.1283.g2c56c4202c5\n> \n\n-- \nDanh\n"},{"id":"460717","messageId":"220805.86v8r6r8in.gmgdl@evledraar.gmail.com","threadId":"58271","inReplyTo":"Yu08MCCoGwTxeM+g@danh.dev","subject":"Re: [PATCH] hook API: don't segfault on strbuf_addf() to NULL \"out\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-05T15:51:17Z","receivedAt":"2022-08-05T15:56:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Aug 05 2022, Đoàn Trần Công Danh wrote:\n\n> On 2022-08-05 16:15:33+0200, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> [...]\n>> +test_expect_success 'git hook run a hook with a bad shebang' '\n>> +\ttest_when_finished \"rm -rf bad-hooks\" &&\n>> +\tmkdir bad-hooks &&\n>> +\twrite_script bad-hooks/test-hook \"/bad/path/no/spaces\" </dev/null &&\n>> +\n>> +\t# TODO: We should emit the same (or at least a more similar)\n>> +\t# error on Windows and !Windows. See the OS-specific code in\n>> +\t# start_command()\n>> +\tif test_have_prereq !WINDOWS\n>> +\tthen\n>> +\t\tcat >expect <<-\\EOF\n>> +\t\tfatal: cannot run bad-hooks/test-hook: ...\n>> +\t\tEOF\n>> +\telse\n>> +\t\tcat >expect <<-\\EOF\n>> +\t\terror: cannot spawn bad-hooks/test-hook: ...\n>> +\t\tEOF\n>> +\tfi &&\n>> +\ttest_expect_code 1 git \\\n>> +\t\t-c core.hooksPath=bad-hooks \\\n>> +\t\thook run test-hook >out 2>err &&\n>> +\ttest_must_be_empty out &&\n>> +\tsed -e \"s/test-hook: .*/test-hook: .../\" <err >actual &&\n>\n> If we're using \"sed\" here, can we also s/cannot run/cannot spawn/\n> in order to have the same expectation?\n>\n> Otherwise, the fix looks sane to me (obviously, since I also suggest\n> removing the line entirely).\n\nWe could, but then we'd miss some weird regression where we changed the\nnon-Windows message to \"fatal: cannot spawn\", which neither emit now.\n\nWe could of course do the \"sed\"-ing in an identical \"test_have_prereq\"\nblock, but I think we'd just be back to square one then, and it would be\nmore readable to just \"cat\" what we expect to happen there.\n\nSo I think I'd prefer to keep it as-is, it also makes a subsequent\nchange where we unify these error messages more obvious, i.e. we'd keep\nthe !WINDOWS branch of that if/else in thath case.\n\n> Reviewed-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n\nThanks!\n"},{"id":"460740","messageId":"xmqqedxuz9cb.fsf@gitster.g","threadId":"58271","inReplyTo":"patch-1.1-2450e3e65cf-20220805T141402Z-avarab@gmail.com","subject":"Re: [PATCH] hook API: don't segfault on strbuf_addf() to NULL \"out\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-05T21:09:08Z","receivedAt":"2022-08-05T21:09:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Fix a logic error in a082345372e (hook API: fix v2.36.0 regression:\n> hooks should be connected to a TTY, 2022-06-07). When it started using\n> the \"ungroup\" API added in fd3aaf53f71 (run-command: add an \"ungroup\"\n> option to run_process_parallel(), 2022-06-07) it should have made the\n> same sort of change that fd3aaf53f71 itself made in\n> \"t/helper/test-run-command.c\".\n>\n> The correct way to emit this \"Couldn't start\" output with \"ungroup\"\n> would be:\n>\n> \tfprintf(stderr, _(\"Couldn't start hook '%s'\\n\"), hook_path);\n>\n> But we should instead remove the emitting of this output. As the added\n> test shows we already emit output when we can't run the child. The\n> \"cannot run\" output here is emitted by run-command.c's\n> child_err_spew().\n>\n> So the addition of the \"Couldn't start hook\" output here in\n> 96e7225b310 (hook: add 'run' subcommand, 2021-12-22) was always\n> redundant. For the pre-commit hook we'll now emit exactly the same\n> output as we did before f443246b9f2 (commit: convert\n> {pre-commit,prepare-commit-msg} hook to hook.h, 2021-12-22) (and\n> likewise for others).\n>\n> We could at this point add this to the pick_next_hook() callbacks in\n> hook.c:\n>\n> \tassert(!out);\n> \tassert(!*pp_task_cb);\n>\n> And this to notify_start_failure() and notify_hook_finished() (in the\n> latter case the parameter is called \"pp_task_cp\"):\n>\n> \tassert(!out);\n> \tassert(!pp_task_cb);\n>\n> But let's leave any such instrumentation for some eventual cleanup of\n> the \"ungroup\" API.\n>\n> Reported-by: Ilya K <me@0upti.me>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>\n> As suspected I needed to bounce this off the CI once to due to the\n> OS-specific messaging around failed hooks, it passes now:\n> https://github.com/avar/git/actions/runs/2802782493\n\nEmily, other than the added code to deal with the difference in\nexpected error message between Windows and other platforms, and the\nname of the temporary file used by the test to capture the raw error\noutput, both in the t1800, everything is the same as the one you\ngave your Reviewed-by earlier, which I'll transplant while queuing\nthis round.\n\nÆvar, thanks for a quick fix.  This will have to go on top of\nab/hooks-regresion-fix topic and downmerged to 2.36 maintenance\ntrack.\n\nThanks, all.\n\n>  hook.c          |  7 -------\n>  t/t1800-hook.sh | 26 ++++++++++++++++++++++++++\n>  2 files changed, 26 insertions(+), 7 deletions(-)\n>\n> diff --git a/hook.c b/hook.c\n> index d113ee7faae..a493939a4fc 100644\n> --- a/hook.c\n> +++ b/hook.c\n> @@ -62,9 +62,6 @@ static int pick_next_hook(struct child_process *cp,\n>  \tstrvec_push(&cp->args, hook_path);\n>  \tstrvec_pushv(&cp->args, hook_cb->options->args.v);\n>  \n> -\t/* Provide context for errors if necessary */\n> -\t*pp_task_cb = (char *)hook_path;\n> -\n>  \t/*\n>  \t * This pick_next_hook() will be called again, we're only\n>  \t * running one hook, so indicate that no more work will be\n> @@ -80,13 +77,9 @@ static int notify_start_failure(struct strbuf *out,\n>  \t\t\t\tvoid *pp_task_cp)\n>  {\n>  \tstruct hook_cb_data *hook_cb = pp_cb;\n> -\tconst char *hook_path = pp_task_cp;\n>  \n>  \thook_cb->rc |= 1;\n>  \n> -\tstrbuf_addf(out, _(\"Couldn't start hook '%s'\\n\"),\n> -\t\t    hook_path);\n> -\n>  \treturn 1;\n>  }\n>  \n> diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh\n> index 210f4298872..64096adac7e 100755\n> --- a/t/t1800-hook.sh\n> +++ b/t/t1800-hook.sh\n> @@ -151,4 +151,30 @@ test_expect_success TTY 'git commit: stdout and stderr are connected to a TTY' '\n>  \ttest_hook_tty commit -m\"B.new\"\n>  '\n>  \n> +test_expect_success 'git hook run a hook with a bad shebang' '\n> +\ttest_when_finished \"rm -rf bad-hooks\" &&\n> +\tmkdir bad-hooks &&\n> +\twrite_script bad-hooks/test-hook \"/bad/path/no/spaces\" </dev/null &&\n> +\n> +\t# TODO: We should emit the same (or at least a more similar)\n> +\t# error on Windows and !Windows. See the OS-specific code in\n> +\t# start_command()\n> +\tif test_have_prereq !WINDOWS\n> +\tthen\n> +\t\tcat >expect <<-\\EOF\n> +\t\tfatal: cannot run bad-hooks/test-hook: ...\n> +\t\tEOF\n> +\telse\n> +\t\tcat >expect <<-\\EOF\n> +\t\terror: cannot spawn bad-hooks/test-hook: ...\n> +\t\tEOF\n> +\tfi &&\n> +\ttest_expect_code 1 git \\\n> +\t\t-c core.hooksPath=bad-hooks \\\n> +\t\thook run test-hook >out 2>err &&\n> +\ttest_must_be_empty out &&\n> +\tsed -e \"s/test-hook: .*/test-hook: .../\" <err >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>  test_done\n"}]}