aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDerrick Stolee <stolee@gmail.com>2024-04-30 16:58:52 +0000
committerJunio C Hamano <gitster@pobox.com>2024-04-30 11:53:18 -0700
commit9a32cb053e3308861ac6b3f41f5a6d9213c0ab84 (patch)
tree4801ca7bba38507832813745886de84db3858fdd
parent786a3e4b8d754d2b14b1208b98eeb0a554ef19a8 (diff)
downloadgit-9a32cb053e3308861ac6b3f41f5a6d9213c0ab84.tar.gz
scalar: avoid segfault in reconfigure --all
Notice: this object is not reachable from any branch.
During the latest v2.45.0 update, 'scalar reconfigure --all' started to segfault on my machine. Breaking it down via the debugger, it was faulting on a NULL reference to the_hash_algo, which is a macro pointing to the_repository->hash_algo. This NULL reference appears to be due to the way the loop is abusing the the_repository pointer, pointing it to a local repository struct after discovering that the current directory is a valid Git repository. This repo-swapping bit was in the original implementation from 4582676075 (scalar: teach 'reconfigure' to optionally handle all registered enlistments, 2021-12-03), but only recently started segfaulting while trying to parse the HEAD reference. This also only happens on the _second_ repository in the list, so does not reproduce if there is only one registered repo. My first inclination was to try to refactor cmd_reconfigure() to execute 'git for-each-repo' instead of this loop. In addition to the difficulty of executing 'scalar reconfigure' within 'git for-each-repo', it would be difficult to perform the clean-up logic for non-existent repos if we relied on that child process. Instead, I chose to move the temporary repo to be within the loop and reinstate the_repository to its old value after we are done performing logic on the current array item. Add a test to t9210-scalar.sh to test 'scalar reconfigure --all' with multiple registered repos, as a precaution. Unfortunately, I was unable to reproduce the segfault using this test, so there is some coverage left to be desired. What exactly causes my setup to hit this bug but not this test structure? Unclear. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Notice: this object is not reachable from any branch.
-rw-r--r--scalar.c10
-rwxr-xr-xt/t9210-scalar.sh17
2 files changed, 24 insertions, 3 deletions
diff --git a/scalar.c b/scalar.c
index fb2940c2a0..7234049a1b 100644
--- a/scalar.c
+++ b/scalar.c
@@ -645,7 +645,6 @@ static int cmd_reconfigure(int argc, const char **argv)
};
struct string_list scalar_repos = STRING_LIST_INIT_DUP;
int i, res = 0;
- struct repository r = { NULL };
struct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;
argc = parse_options(argc, argv, NULL, options,
@@ -665,6 +664,7 @@ static int cmd_reconfigure(int argc, const char **argv)
for (i = 0; i < scalar_repos.nr; i++) {
int succeeded = 0;
+ struct repository *old_repo, r = { NULL };
const char *dir = scalar_repos.items[i].string;
strbuf_reset(&commondir);
@@ -712,13 +712,17 @@ static int cmd_reconfigure(int argc, const char **argv)
git_config_clear();
+ if (repo_init(&r, gitdir.buf, commondir.buf))
+ goto loop_end;
+
+ old_repo = the_repository;
the_repository = &r;
- r.commondir = commondir.buf;
- r.gitdir = gitdir.buf;
if (set_recommended_config(1) >= 0)
succeeded = 1;
+ the_repository = old_repo;
+
loop_end:
if (!succeeded) {
res = -1;
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
index 428339e342..a696337b05 100755
--- a/t/t9210-scalar.sh
+++ b/t/t9210-scalar.sh
@@ -180,6 +180,23 @@ test_expect_success 'scalar reconfigure' '
test true = "$(git -C one/src config core.preloadIndex)"
'
+test_expect_success 'scalar reconfigure --all' '
+ repos="two three four" &&
+ for num in $repos
+ do
+ git init $num/src &&
+ scalar register $num/src &&
+ git -C $num/src config core.preloadIndex false || return 1
+ done &&
+
+ scalar reconfigure --all &&
+
+ for num in $repos
+ do
+ test true = "$(git -C $num/src config core.preloadIndex)" || return 1
+ done
+'
+
test_expect_success '`reconfigure -a` removes stale config entries' '
git init stale/src &&
scalar register stale &&