summaryrefslogtreecommitdiff
path: root/worktree.c
diff options
context:
space:
mode:
authorJeff King <peff@peff.net>2017-10-19 17:49:36 (GMT)
committerJunio C Hamano <gitster@pobox.com>2017-10-21 12:30:07 (GMT)
commitdbd2b55cb7b06e94096b8c18852a94732e3f76a8 (patch)
treec21e44cdfecca38a1b2c63a9b9061faa3ed509e2 /worktree.c
parentd79be4983bdf6598f106710a4826752a96f5dd58 (diff)
downloadgit-dbd2b55cb7b06e94096b8c18852a94732e3f76a8.zip
git-dbd2b55cb7b06e94096b8c18852a94732e3f76a8.tar.gz
git-dbd2b55cb7b06e94096b8c18852a94732e3f76a8.tar.bz2
worktree: handle broken symrefs in find_shared_symref()
The refs_resolve_ref_unsafe() function may return NULL even with a REF_ISSYMREF flag if a symref points to a broken ref. As a result, it's possible for find_shared_symref() to segfault when it passes NULL to strcmp(). This is hard to trigger for most code paths. We typically pass HEAD to the function as the symref to resolve, and programs like "git branch" will bail much earlier if HEAD isn't valid. I did manage to trigger it through one very obscure sequence: # You have multiple notes refs which conflict. git notes add -m base git notes --ref refs/notes/foo add -m foo # There's left-over cruft in NOTES_MERGE_REF that # makes it a broken symref (in this case we point # to a syntactically invalid ref). echo "ref: refs/heads/master.lock" >.git/NOTES_MERGE_REF # You try to merge the notes. We read the broken value in # order to complain that another notes-merge is # in-progress, but we segfault in find_shared_symref(). git notes merge refs/notes/foo This is obviously silly and almost certainly impossible to trigger accidentally, but it does show that the bug is triggerable from at least one code path. In addition, it would trigger if we saw a transient filesystem error when resolving the pointed-to ref. We can fix this by treating NULL the same as a non-matching symref. Arguably we'd prefer to know if a symref points to "refs/heads/foo", but "refs/heads/foo" is broken. But refs_resolve_ref_unsafe() isn't capable of giving us that information, so this is the best we can do. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'worktree.c')
-rw-r--r--worktree.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/worktree.c b/worktree.c
index 7001562..f8c40f2 100644
--- a/worktree.c
+++ b/worktree.c
@@ -327,7 +327,8 @@ const struct worktree *find_shared_symref(const char *symref,
refs = get_worktree_ref_store(wt);
symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
NULL, &flags);
- if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
+ if ((flags & REF_ISSYMREF) &&
+ symref_target && !strcmp(symref_target, target)) {
existing = wt;
break;
}