fix(clone): segment fault when using --revision and protocol v0/v1
PATCH 1/1 fix(clone): segment fault when using --revision and protocol v0/v1
From: Nitro Cao <[email protected]>
git clone command would segment fault when satisfying the following conditions at the same time:
- Use HTTP protocol v0 or v1 to interact with remote servers.
- The value of --revision doesn't specify the peer reference, like --revision master instead of --revision refs/heads/master:master
When using protocol v2, git client can use ref-prefix param of ls-refs command to fetch wanted references based on --revision. But for protocol v0/v1, git client just fetch all references and doesn't filter them.
In this case, the value of remote_head variable is not NULL, which leads to the value of remote_head_points_at not NULL too. But we don't specify the peer reference in --revsion, remote_head_points_at->peer_ref would be NULL. So git client would boom when update_remote_refs.
---
fix(clone): segment fault when using --revision and protocol v0/v1
git clone command would segment fault when satisfying the following conditions at the same time:
* Use HTTP protocol v0 or v1 to interact with remote servers.
* The value of --revision doesn't specify the peer reference, like --revision master instead of --revision refs/heads/master:master
When using protocol v2, git client can use ref-prefix param of ls-refs command to fetch wanted references based on --revision. But for protocol v0/v1, git client just fetch all references and doesn't filter them. In this case, the value of remote_head variable is not NULL, which leads to the value of remote_head_points_at not NULL too. But we don't specify the peer reference in --revsion, remote_head_points_at->peer_ref would be NULL. So git client would boom when update_remote_refs.
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2185%2FNitroCao%2Ffix%2Fsegment-fault-with-revision-param-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2185/NitroCao/fix/segment-fault-with-revision-param-v1
Pull-Request: https://github.com/git/git/pull/2185
builtin/clone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
builtin/clone.c +1 -1
@@ -558,7 +558,7 @@ static void update_remote_refs(const struct ref *refs,write_followtags (refs, msg); } -if (remote_head_points_at && !option_bare) { +if (remote_head_points_at && remote_head_points_at->peer_ref && !option_bare) {struct strbuf head_ref =STRBUF_INIT ;strbuf_addstr (&head_ref, branch_top);strbuf_addstr (&head_ref,"HEAD" );
base-commit: 22584464849815268419fd9d2eba307362360db1
Signature · 1 line
gitgitgadget
PATCH v2 1/1 clone: fix segfault when using --revision and v0/v1 protocol
From: Nitro Cao <[email protected]>
When git clone is used with --revision and the protocol version is v0 or v1, the client segfaults if the revision does not specify a peer reference (e.g. --revision master instead of--revision refs/heads/master:master).
This occurs because update_remote_refs() assumes that if remote_head_points_at is set, remote_head_points_at->peer_ref is also valid. However, for v0/v1 protocols, all references are fetched without filtering, and if the revision lacks a peer reference, peer_ref remains NULL.
Add a check for remote_head_points_at->peer_ref before dereferencing it to prevent the segmentation fault.
---
fix(clone): segment fault when using --revision and protocol v0/v1
git clone command would segment fault when satisfying the following conditions at the same time:
* Use HTTP protocol v0 or v1 to interact with remote servers.
* The value of --revision doesn't specify the peer reference, like --revision master instead of --revision refs/heads/master:master
When using protocol v2, git client can use ref-prefix param of ls-refs command to fetch wanted references based on --revision. But for protocol v0/v1, git client just fetch all references and doesn't filter them. In this case, the value of remote_head variable is not NULL, which leads to the value of remote_head_points_at not NULL too. But we don't specify the peer reference in --revsion, remote_head_points_at->peer_ref would be NULL. So git client would boom when update_remote_refs.
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2185%2FNitroCao%2Ffix%2Fsegment-fault-with-revision-param-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2185/NitroCao/fix/segment-fault-with-revision-param-v2
Pull-Request: https://github.com/git/git/pull/2185
Range-diff vs v1:
1: f75b1f7e2e ! 1: b3ab27f977 fix(clone): segment fault when using --revision and protocol v0/v1
@@ Metadata
Author: Nitro Cao <[email protected]>
## Commit message ##
- fix(clone): segment fault when using --revision and protocol v0/v1
+ clone: fix segfault when using --revision and v0/v1 protocol
- git clone command would segment fault when satisfying the following
- conditions at the same time:
- - Use HTTP protocol v0 or v1 to interact with remote servers.
- - The value of --revision doesn't specify the peer reference, like
- --revision master instead of --revision refs/heads/master:master
+ When git clone is used with --revision and the protocol version is
+ v0 or v1, the client segfaults if the revision does not specify a peer
+ reference (e.g. --revision master instead of
+ --revision refs/heads/master:master).
- When using protocol v2, git client can use ref-prefix param of
- ls-refs command to fetch wanted references based on --revision.
- But for protocol v0/v1, git client just fetch all references and
- doesn't filter them.
- In this case, the value of remote_head variable is not NULL,
- which leads to the value of remote_head_points_at not NULL too.
- But we don't specify the peer reference in --revsion,
- remote_head_points_at->peer_ref would be NULL. So git client would
- boom when update_remote_refs.
+ This occurs because update_remote_refs() assumes that if
+ remote_head_points_at is set, remote_head_points_at->peer_ref is
+ also valid. However, for v0/v1 protocols, all references are fetched
+ without filtering, and if the revision lacks a peer reference,
+ peer_ref remains NULL.
+
+ Add a check for remote_head_points_at->peer_ref before dereferencing
+ it to prevent the segmentation fault.
builtin/clone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
builtin/clone.c +1 -1
@@ -558,7 +558,7 @@ static void update_remote_refs(const struct ref *refs,write_followtags (refs, msg); } -if (remote_head_points_at && !option_bare) { +if (remote_head_points_at && remote_head_points_at->peer_ref && !option_bare) {struct strbuf head_ref =STRBUF_INIT ;strbuf_addstr (&head_ref, branch_top);strbuf_addstr (&head_ref,"HEAD" );
base-commit: 67ad42147a7acc2af6074753ebd03d904476118f
Signature · 1 line
gitgitgadget
Re: [PATCH v2] clone: fix segfault when using --revision and v0/v1 protocol
Quoted 7 lines · NitroCao via GitGitGadget
From: Nitro Cao <[email protected]>
When
git cloneis used with--revisionand the protocol version is v0 or v1, the client segfaults if the revision does not specify a peer reference (e.g.--revision masterinstead of--revision refs/heads/master:master).
Quoted 8 lines
This occurs because
update_remote_refs()assumes that ifremote_head_points_atis set,remote_head_points_at->peer_refis also valid. However, for v0/v1 protocols, all references are fetched without filtering, and if the revision lacks a peer reference,peer_refremains NULL.Add a check for
remote_head_points_at->peer_refbefore dereferencing it to prevent the segmentation fault.
Hmph.
While your change may skip the code that segfaults, wouldn't it also stop noticing a broken case where .peer_ref should have been set but didn't, even when --revision=<rev> parameter is not used in the command invocation? IOW, it is better to segfault and draw attention by Git developers when a valid input is given by the end user and our code misbehaves (e.g., and fails to to set .peer_ref as it should).
Stepping back a bit, "git clone --help" says the following on "--revision=<rev>":
--revision=<rev>::
Create a new repository, and fetch the history leading to the given revision _<rev>_ (and nothing else), without making any remote-tracking branch, and without making any local branch, and detach HEAD to _<rev>_. The argument can be a ref name (e.g. refs/heads/main or refs/tags/v1.0) that peels down to a commit, or a hexadecimal object name. This option is incompatible with --branch and --mirror.
The intent of running the command with this option seems to me that we do not want to create any branches, neither remote-tracking nor local. Looking at what is done in update_remote_refs(), I think we still want to honor check_connectivity even when we are in this "single revision only, detach the HEAD at that commit" mode in order to ensure the integrity of the data, but we cetainly do not want to call the write_remote_refs() and the write_followtags() helpers.
Wouldn't the correct fix be more like the following?
- split out parts from update_remote_refs() that are needed even in option_rev mode into a separate helper function, and call that from cmd_clone().
- make the call to update_remote_refs() conditional---specifically, we shouldn't be calling it when option_rev is in effect.
Quoted 2 lines
builtin/clone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Also, isn't this something we can specify the expected behaviour in tests? Not only we want to ensure that nothing segfaults, we would want to make sure that the resulting repository has no refs and HEAD is detached at the specified revision.
Thanks.
Quoted 15 lines
builtin/clone.c +1
-1@@ -558,7 +558,7 @@ static void update_remote_refs(const struct ref *refs,write_followtags (refs, msg); } -if (remote_head_points_at && !option_bare) { +if (remote_head_points_at && remote_head_points_at->peer_ref && !option_bare) {struct strbuf head_ref =STRBUF_INIT ;strbuf_addstr (&head_ref, branch_top);strbuf_addstr (&head_ref,"HEAD" );base-commit:
67ad42147a7acc2af6074753ebd03d904476118f
Hi Junio and thanks for your time! I'm a newbie, sorry for the last reply :(
Quoted 6 lines
While your change may skip the code that segfaults, wouldn't it also stop noticing a broken case where .peer_ref should have been set but didn't, even when --revision=<rev> parameter is not used in the command invocation? IOW, it is better to segfault and draw attention by Git developers when a valid input is given by the end user and our code misbehaves (e.g., and fails to to set .peer_ref as it should).
I totally agree with you.
Quoted 8 lines
Wouldn't the correct fix be more like the following?
- split out parts from
update_remote_refs()that are needed even inoption_revmode into a separate helper function, and call that fromcmd_clone().- make the call to
update_remote_refs()conditional---specifically, we shouldn't be calling it whenoption_revis in effect.
Do we have another fix to make a conditional call to find_ref_by_name() when option_rev is in effect? Because from the doc of --revision, "... and detach HEAD to_<rev>_. ...", that means we don't need to know where the real HEAD points to?
Quoted 4 lines
Also, isn't this something we can specify the expected behaviour in tests? Not only we want to ensure that nothing segfaults, we would want to make sure that the resulting repository has no refs and HEAD is detached at the specified revision.
I'll add several tests to cover the bug after we make sure how to fix it.