On Wed, Jul 11, 2018 at 01:00:57PM +0200, Ævar Arnfjörð Bjarmason wrote: > This segfaults, but should print an error instead, have a repo with a > corrupt HEAD: > > ( > rm -rf /tmp/git && > git clone --single-branch --branch todo git@xxxxxxxxxx:git/git.git /tmp/git && > echo 1111111111111111111111111111111111111111 >/tmp/git/.git/refs/heads/todo && > git -C /tmp/git pull > ) It took me a minute to reproduce this. It needs "pull --rebase" if you don't have that setup in your config. > The immediate reason is that in run_diff_index() we have this: > > ent = revs->pending.objects; > > And that in this case that's NULL: > > (gdb) bt > #0 0x000055555565993f in run_diff_index (revs=0x7fffffffcb90, cached=1) at diff-lib.c:524 > #1 0x00005555557633da in has_uncommitted_changes (ignore_submodules=1) at wt-status.c:2345 These two are the interesting functions. has_uncommitted_changes() calls add_head_to_pending(). So it could realize then that there is no valid HEAD to compare against. But as you note, it's run_diff_index() that blindly dereferences revs->pending.objects without seeing if it's non-empty. Normally setup_revisions() would barf on a bad object, but the manual add_head_to_pending() quietly returns (as it must for some cases, like unborn branches). So I feel like the right answer here is probably this: diff --git a/wt-status.c b/wt-status.c index d1c05145a4..5fcaa3d0f8 100644 --- a/wt-status.c +++ b/wt-status.c @@ -2340,7 +2340,16 @@ int has_uncommitted_changes(int ignore_submodules) if (ignore_submodules) rev_info.diffopt.flags.ignore_submodules = 1; rev_info.diffopt.flags.quick = 1; + add_head_to_pending(&rev_info); + if (!rev_info.pending.nr) { + /* + * We have no head (or it's corrupt), but the index is not + * unborn; declare it as uncommitted changes. + */ + return 1; + } + diff_setup_done(&rev_info.diffopt); result = run_diff_index(&rev_info, 1); return diff_result_code(&rev_info.diffopt, result); That does quietly paper over the corruption, but it does the conservative thing, and a follow-up "git status" would yield "bad object: HEAD". I do worry that other callers of run_diff_index() might have similar problems, though. Grepping around, the other callers seem to fall into one of three categories: - they resolve the object themselves and put it in the pending list (and often fallback to the empty tree, which is more or less what the patch above is doing) - they resolve the object themselves and avoid calling run_diff_index() if it's not valid - they use setup_revisions(), which will barf on the broken object So I think this may be sufficient. We probably should also add an assertion to run_diff_index(), since that's better than segfaulting. -Peff