* [PATCH v=2 0/1] files-backend: check symref name before update @ 2025-10-04 14:42 Han Young 2025-10-04 14:42 ` [PATCH v=2 1/1] " Han Young 0 siblings, 1 reply; 5+ messages in thread From: Han Young @ 2025-10-04 14:42 UTC (permalink / raw) To: git; +Cc: karthik.188, gitster, ps, Han Young From: Han Young <hanyoung@protonmail.com> Following the discussing, I looked into the refs verify and fsck commands. The refs verify command does not check the HEAD ref, fsck checks the HEAD ref name and the commit object it points to. I think we can move the fsck_head_link from fsck to refs. Then both git fsck and git refs verify can use the same function to check the HEAD ref. However, there's no harm in adding a runtime check. 'git reset foo' will happily create a ref file outside refs/ and return without error on an invalid HEAD ref. Change from v1: * add a test in t7102-reset Han Young (1): files-backend: check symref name before update refs/files-backend.c | 10 ++++++++++ t/t7102-reset.sh | 8 ++++++++ 2 files changed, 18 insertions(+) -- 2.51.0.373.g2c26b26d9 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v=2 1/1] files-backend: check symref name before update 2025-10-04 14:42 [PATCH v=2 0/1] files-backend: check symref name before update Han Young @ 2025-10-04 14:42 ` Han Young 2025-10-05 21:53 ` Junio C Hamano 0 siblings, 1 reply; 5+ messages in thread From: Han Young @ 2025-10-04 14:42 UTC (permalink / raw) To: git; +Cc: karthik.188, gitster, ps, Han Young, Sigma From: Han Young <hanyoung@protonmail.com> In the ref files backend, the symbolic reference name is not checked before an update. This could cause reference and lock files to be created outside the refs/ directory. Validate the reference before adding it to the ref update transaction. Reported-by: Sigma <git@sigma-star.io> Signed-off-by: Han Young <hanyoung@protonmail.com> --- refs/files-backend.c | 10 ++++++++++ t/t7102-reset.sh | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/refs/files-backend.c b/refs/files-backend.c index bc3347d18..d47a8c392 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2516,6 +2516,16 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update, struct ref_update *new_update; unsigned int new_flags; + /* + * Check the referent is valid before adding it to the transaction. + */ + if (!refname_is_safe(referent)) { + strbuf_addf(err, + "reference '%s' appears to be broken", + update->refname); + return -1; + } + /* * First make sure that referent is not already in the * transaction. This check is O(lg N) in the transaction diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 0503a64d3..1dc314474 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -634,4 +634,12 @@ test_expect_success 'reset handles --end-of-options' ' test_cmp expect actual ' +test_expect_success 'reset should fail when HEAD is corrupt' ' + head=$(cat .git/HEAD) && + hex=$(git log -1 --format="%h") && + echo "ref: refs/../foo" > .git/HEAD && + test_must_fail git reset $hex && + echo $head > .git/HEAD +' + test_done -- 2.51.0.373.g2c26b26d9 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v=2 1/1] files-backend: check symref name before update 2025-10-04 14:42 ` [PATCH v=2 1/1] " Han Young @ 2025-10-05 21:53 ` Junio C Hamano 2025-10-06 0:46 ` Jeff King 0 siblings, 1 reply; 5+ messages in thread From: Junio C Hamano @ 2025-10-05 21:53 UTC (permalink / raw) To: Han Young; +Cc: git, karthik.188, ps, Han Young, Sigma Han Young <hanyang.tony@bytedance.com> writes: > From: Han Young <hanyoung@protonmail.com> > > In the ref files backend, the symbolic reference name is not checked > before an update. This could cause reference and lock files to be created > outside the refs/ directory. Validate the reference before adding it to > the ref update transaction. This leaves the readers wondering why refname_is_safe(), which has no direct callers other than "git show-ref verify", is sufficient for the purpose of this particular validation. All other callers of refname_is_safe() seem to use it only as a sanity check combined with other criteria. For example, refs.c::transaction_refname_valid() calls refname_is_safe() as a small part of its validation, together with check_refname_format(). It also refuses to touch anything that satisfies is_pseudo_ref(). > Reported-by: Sigma <git@sigma-star.io> > Signed-off-by: Han Young <hanyoung@protonmail.com> > --- > refs/files-backend.c | 10 ++++++++++ > t/t7102-reset.sh | 8 ++++++++ > 2 files changed, 18 insertions(+) > > diff --git a/refs/files-backend.c b/refs/files-backend.c > index bc3347d18..d47a8c392 100644 > --- a/refs/files-backend.c > +++ b/refs/files-backend.c > @@ -2516,6 +2516,16 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update, > struct ref_update *new_update; > unsigned int new_flags; > > + /* > + * Check the referent is valid before adding it to the transaction. > + */ > + if (!refname_is_safe(referent)) { > + strbuf_addf(err, > + "reference '%s' appears to be broken", > + update->refname); > + return -1; > + } > + > /* > * First make sure that referent is not already in the > * transaction. This check is O(lg N) in the transaction > diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh > index 0503a64d3..1dc314474 100755 > --- a/t/t7102-reset.sh > +++ b/t/t7102-reset.sh > @@ -634,4 +634,12 @@ test_expect_success 'reset handles --end-of-options' ' > test_cmp expect actual > ' > > +test_expect_success 'reset should fail when HEAD is corrupt' ' > + head=$(cat .git/HEAD) && > + hex=$(git log -1 --format="%h") && > + echo "ref: refs/../foo" > .git/HEAD && > + test_must_fail git reset $hex && > + echo $head > .git/HEAD > +' > + > test_done ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v=2 1/1] files-backend: check symref name before update 2025-10-05 21:53 ` Junio C Hamano @ 2025-10-06 0:46 ` Jeff King 2025-10-06 15:52 ` Junio C Hamano 0 siblings, 1 reply; 5+ messages in thread From: Jeff King @ 2025-10-06 0:46 UTC (permalink / raw) To: Junio C Hamano; +Cc: Han Young, git, karthik.188, ps, Han Young, Sigma On Sun, Oct 05, 2025 at 02:53:39PM -0700, Junio C Hamano wrote: > Han Young <hanyang.tony@bytedance.com> writes: > > > From: Han Young <hanyoung@protonmail.com> > > > > In the ref files backend, the symbolic reference name is not checked > > before an update. This could cause reference and lock files to be created > > outside the refs/ directory. Validate the reference before adding it to > > the ref update transaction. > > This leaves the readers wondering why refname_is_safe(), which has > no direct callers other than "git show-ref verify", is sufficient > for the purpose of this particular validation. All other callers of > refname_is_safe() seem to use it only as a sanity check combined > with other criteria. > > For example, refs.c::transaction_refname_valid() calls > refname_is_safe() as a small part of its validation, together with > check_refname_format(). It also refuses to touch anything that > satisfies is_pseudo_ref(). Yes, if we wanted to add a check here, it should be doing the usual check for a syntactically valid refname and falling back to refname_is_safe() only for deletions. But I'm not sure if this check is that valuable. We are in split_symref_update(), which takes an update to some symref and splits it into an update to that symref's reflog and a real update to the underlying target ref. So we are not checking input to the transaction here, but the existing state of the symref on disk. And in theory we should have checked that target already when we wrote it. Do we want to check it again? I dunno. I can see an argument for being overly paranoid (garbage snuck in somehow, but we prefer not to act on it). We do already check sanity within resolve_ref_unsafe(), for example. I do think there are also some gaps in our symref target checks (as well as a few other spots). I have a series to fix those that just needs a little bit of polishing, and hopefully can send out this coming week. > > diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh > > index 0503a64d3..1dc314474 100755 > > --- a/t/t7102-reset.sh > > +++ b/t/t7102-reset.sh > > @@ -634,4 +634,12 @@ test_expect_success 'reset handles --end-of-options' ' > > test_cmp expect actual > > ' > > > > +test_expect_success 'reset should fail when HEAD is corrupt' ' > > + head=$(cat .git/HEAD) && > > + hex=$(git log -1 --format="%h") && > > + echo "ref: refs/../foo" > .git/HEAD && > > + test_must_fail git reset $hex && > > + echo $head > .git/HEAD > > +' This test won't work against the reftable backend. -Peff ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v=2 1/1] files-backend: check symref name before update 2025-10-06 0:46 ` Jeff King @ 2025-10-06 15:52 ` Junio C Hamano 0 siblings, 0 replies; 5+ messages in thread From: Junio C Hamano @ 2025-10-06 15:52 UTC (permalink / raw) To: Jeff King; +Cc: Han Young, git, karthik.188, ps, Han Young, Sigma Jeff King <peff@peff.net> writes: >> This leaves the readers wondering why refname_is_safe(), which has >> no direct callers other than "git show-ref verify", is sufficient >> for the purpose of this particular validation. All other callers of >> refname_is_safe() seem to use it only as a sanity check combined >> with other criteria. >> >> For example, refs.c::transaction_refname_valid() calls >> refname_is_safe() as a small part of its validation, together with >> check_refname_format(). It also refuses to touch anything that >> satisfies is_pseudo_ref(). > > Yes, if we wanted to add a check here, it should be doing the usual > check for a syntactically valid refname and falling back to > refname_is_safe() only for deletions. > > But I'm not sure if this check is that valuable. We are in > split_symref_update(), which takes an update to some symref and splits > it into an update to that symref's reflog and a real update to the > underlying target ref. So we are not checking input to the transaction > here, but the existing state of the symref on disk. And in theory we > should have checked that target already when we wrote it. Yes, it was Karthik, I think, who pointed out in the ealier round that the set-up procedure used to demonstrate the issue indicated that it was essentially a corrupt repository doing an unexpected thing, and I tend to agree. What you wrote in the previous paragraph matches the reason why I questioned "is this enough?" > I do think there are also some gaps in our symref target checks (as well > as a few other spots). I have a series to fix those that just needs a > little bit of polishing, and hopefully can send out this coming week. Thanks, looking forward to reading them. ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-10-06 15:52 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-10-04 14:42 [PATCH v=2 0/1] files-backend: check symref name before update Han Young 2025-10-04 14:42 ` [PATCH v=2 1/1] " Han Young 2025-10-05 21:53 ` Junio C Hamano 2025-10-06 0:46 ` Jeff King 2025-10-06 15:52 ` Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).