From d399490706c2325d13dc1ce1f45158b33ff5cfd5 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 8 Oct 2024 14:15:53 +0200 Subject: [PATCH] builtin/gc: fix crash when running `git maintenance start` It was reported on the mailing list that running `git maintenance start` immediately segfaults starting with b6c3f8e12c (builtin/maintenance: fix leak in `get_schedule_cmd()`, 2024-09-26). And indeed, this segfault is trivial to reproduce up to a point where one is scratching their head why we didn't catch this regression in our test suite. The root cause of this error is `get_schedule_cmd()`, which does not populate the `out` parameter in all cases anymore starting with the mentioned commit. Callers do assume it to always be populated though and will e.g. call `strvec_split()` on the returned value, which will of course segfault when the variable is uninitialized. So why didn't we catch this trivial regression? The reason is that our tests always set up the "GIT_TEST_MAINT_SCHEDULER" environment variable via "t/test-lib.sh", which allows us to override the scheduler command with a custom one so that we don't accidentally modify the developer's system. But the faulty code where we don't set the `out` parameter will only get hit in case that environment variable is _not_ set, which is never the case when executing our tests. Fix the regression by again unconditionally allocating the value in the `out` parameter, if provided. Add a test that unsets the environment variable to catch future regressions in this area. Reported-by: Shubham Kanodia Signed-off-by: Patrick Steinhardt Signed-off-by: Derrick Stolee --- builtin/gc.c | 7 +++++-- t/t7900-maintenance.sh | 16 ++++++++++++++++ t/test-lib.sh | 6 ++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/builtin/gc.c b/builtin/gc.c index 6a7a2da006eeee..d52735354c9f87 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1832,7 +1832,7 @@ static const char *get_extra_launchctl_strings(void) { * | Input | Output | * | *cmd | return code | *out | *is_available | * +-------+-------------+-------------------+---------------+ - * | "foo" | false | NULL | (unchanged) | + * | "foo" | false | "foo" (allocated) | (unchanged) | * +-------+-------------+-------------------+---------------+ * * GIT_TEST_MAINT_SCHEDULER set to “foo:./mock_foo.sh,bar:./mock_bar.sh” @@ -1850,8 +1850,11 @@ static int get_schedule_cmd(const char *cmd, int *is_available, char **out) struct string_list_item *item; struct string_list list = STRING_LIST_INIT_NODUP; - if (!testing) + if (!testing) { + if (out) + *out = xstrdup(cmd); return 0; + } if (is_available) *is_available = 0; diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index a66d0e089d2d3a..86bc77e73ffcb2 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -646,6 +646,22 @@ test_expect_success !MINGW 'register and unregister with regex metacharacters' ' maintenance.repo "$(pwd)/$META" ' +test_expect_success !MINGW,!DARWIN 'start without GIT_TEST_MAINT_SCHEDULER' ' + test_when_finished "rm -rf crontab.log script repo" && + mkdir script && + write_script script/crontab <<-EOF && + echo "\$*" >>"$(pwd)"/crontab.log + EOF + git init repo && + ( + cd repo && + sane_unset GIT_TEST_MAINT_SCHEDULER && + PATH="$(pwd)/../script:$PATH" git maintenance start --scheduler=crontab + ) && + test_grep -- -l crontab.log && + test_grep -- git_cron_edit_tmp crontab.log +' + test_expect_success 'start --scheduler=' ' test_expect_code 129 git maintenance start --scheduler=foo 2>err && test_grep "unrecognized --scheduler argument" err && diff --git a/t/test-lib.sh b/t/test-lib.sh index b1a8ee5c002b35..f12f3a76093375 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1715,6 +1715,12 @@ case $uname_s in test_set_prereq GREP_STRIPS_CR test_set_prereq WINDOWS ;; +*Darwin*) + test_set_prereq POSIXPERM + test_set_prereq BSLASHPSPEC + test_set_prereq EXECKEEPSPID + test_set_prereq DARWIN + ;; *) test_set_prereq POSIXPERM test_set_prereq BSLASHPSPEC