git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* "git branch HEAD" dumps core when on detached head (NULL pointer dereference)
@ 2013-02-21 12:27 Per Cederqvist
  2013-02-21 12:50 ` Duy Nguyen
  2013-02-21 14:18 ` [PATCH] branch: segfault fixes and validation Nguyễn Thái Ngọc Duy
  0 siblings, 2 replies; 10+ messages in thread
From: Per Cederqvist @ 2013-02-21 12:27 UTC (permalink / raw)
  To: git

Running "git branch HEAD" may be a stupid thing to do. It actually
was a mistake on my part. Still, I don't think git should dereference
a NULL pointer.

Tested using git 1.8.1.4 adn 1.8.1.1.

Repeat by:

     mkdir branchcrash || exit 1
     cd branchcrash
     git init
     touch a; git add a; git commit -m"Added a".
     touch b; git add b; git commit -m"Added b".
     git checkout HEAD^
     git branch HEAD

The last command dumps core.  gdb session:

gdb /usr/local/bin/git core
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /usr/local/bin/git...done.
[New LWP 7174]

warning: Can't read pathname for load map: Input/output error.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `git branch HEAD'.
Program terminated with signal 11, Segmentation fault.
#0  cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>)
     at builtin/branch.c:919
919			strbuf_addf(&buf, "refs/remotes/%s", branch->name);
(gdb) bt
#0  cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>)
     at builtin/branch.c:919
#1  0x00000000004046ac in run_builtin (argv=0x7fffe6e2a0f0, argc=2,
     p=<optimized out>) at git.c:273
#2  handle_internal_command (argc=2, argv=0x7fffe6e2a0f0) at git.c:434
#3  0x0000000000404df3 in run_argv (argv=0x7fffe6e29f90, 
argcp=0x7fffe6e29f9c)
     at git.c:480
#4  main (argc=2, argv=0x7fffe6e2a0f0) at git.c:555
(gdb) p branch
$1 = (struct branch *) 0x0
(gdb) quit

     /ceder

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: "git branch HEAD" dumps core when on detached head (NULL pointer dereference)
  2013-02-21 12:27 "git branch HEAD" dumps core when on detached head (NULL pointer dereference) Per Cederqvist
@ 2013-02-21 12:50 ` Duy Nguyen
  2013-02-21 13:24   ` Per Cederqvist
  2013-02-21 14:18 ` [PATCH] branch: segfault fixes and validation Nguyễn Thái Ngọc Duy
  1 sibling, 1 reply; 10+ messages in thread
From: Duy Nguyen @ 2013-02-21 12:50 UTC (permalink / raw)
  To: Per Cederqvist; +Cc: git

On Thu, Feb 21, 2013 at 7:27 PM, Per Cederqvist <cederp@opera.com> wrote:
> Running "git branch HEAD" may be a stupid thing to do. It actually
> was a mistake on my part. Still, I don't think git should dereference
> a NULL pointer.

We should not. Can you make a patch to fix it (with test cases)? You
may want to fix the two preceding blocks, "if (new_upstream)" and "if
(unset_upstream)", as well. They don't check for NULL branch either. I
think we can say something like "detached HEAD is not valid for this
operation" before exit.

>
> Tested using git 1.8.1.4 adn 1.8.1.1.
>
> Repeat by:
>
>     mkdir branchcrash || exit 1
>     cd branchcrash
>     git init
>     touch a; git add a; git commit -m"Added a".
>     touch b; git add b; git commit -m"Added b".
>     git checkout HEAD^
>     git branch HEAD
>
> The last command dumps core.  gdb session:
>
> gdb /usr/local/bin/git core
> GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
> Copyright (C) 2012 Free Software Foundation, Inc.
> License GPLv3+: GNU GPL version 3 or later
> <http://gnu.org/licenses/gpl.html>
> This is free software: you are free to change and redistribute it.
> There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
> and "show warranty" for details.
> This GDB was configured as "x86_64-linux-gnu".
> For bug reporting instructions, please see:
> <http://bugs.launchpad.net/gdb-linaro/>...
> Reading symbols from /usr/local/bin/git...done.
> [New LWP 7174]
>
> warning: Can't read pathname for load map: Input/output error.
> [Thread debugging using libthread_db enabled]
> Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
> Core was generated by `git branch HEAD'.
> Program terminated with signal 11, Segmentation fault.
> #0  cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>)
>     at builtin/branch.c:919
> 919                     strbuf_addf(&buf, "refs/remotes/%s", branch->name);
> (gdb) bt
> #0  cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>)
>     at builtin/branch.c:919
> #1  0x00000000004046ac in run_builtin (argv=0x7fffe6e2a0f0, argc=2,
>     p=<optimized out>) at git.c:273
> #2  handle_internal_command (argc=2, argv=0x7fffe6e2a0f0) at git.c:434
> #3  0x0000000000404df3 in run_argv (argv=0x7fffe6e29f90,
> argcp=0x7fffe6e29f9c)
>     at git.c:480
> #4  main (argc=2, argv=0x7fffe6e2a0f0) at git.c:555
> (gdb) p branch
> $1 = (struct branch *) 0x0
> (gdb) quit
>
>     /ceder
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Duy

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: "git branch HEAD" dumps core when on detached head (NULL pointer dereference)
  2013-02-21 12:50 ` Duy Nguyen
@ 2013-02-21 13:24   ` Per Cederqvist
  2013-02-21 13:32     ` Duy Nguyen
  0 siblings, 1 reply; 10+ messages in thread
From: Per Cederqvist @ 2013-02-21 13:24 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: git

On 02/21/13 13:50, Duy Nguyen wrote:
> On Thu, Feb 21, 2013 at 7:27 PM, Per Cederqvist <cederp@opera.com> wrote:
>> Running "git branch HEAD" may be a stupid thing to do. It actually
>> was a mistake on my part. Still, I don't think git should dereference
>> a NULL pointer.
>
> We should not. Can you make a patch to fix it (with test cases)? You
> may want to fix the two preceding blocks, "if (new_upstream)" and "if
> (unset_upstream)", as well. They don't check for NULL branch either. I
> think we can say something like "detached HEAD is not valid for this
> operation" before exit.

Sorry, but isolating the issue reporting it here is about as much time
as I can spend on this issue. Learning the coding standard of Git and
how to write test cases is not something I'm prepared to do, at least
not at the moment. I hope there is a place for users and reporters of
bugs in the Git community.

     /ceder

>>
>> Tested using git 1.8.1.4 adn 1.8.1.1.
>>
>> Repeat by:
>>
>>      mkdir branchcrash || exit 1
>>      cd branchcrash
>>      git init
>>      touch a; git add a; git commit -m"Added a".
>>      touch b; git add b; git commit -m"Added b".
>>      git checkout HEAD^
>>      git branch HEAD
>>
>> The last command dumps core.  gdb session:
>>
>> gdb /usr/local/bin/git core
>> GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
>> Copyright (C) 2012 Free Software Foundation, Inc.
>> License GPLv3+: GNU GPL version 3 or later
>> <http://gnu.org/licenses/gpl.html>
>> This is free software: you are free to change and redistribute it.
>> There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
>> and "show warranty" for details.
>> This GDB was configured as "x86_64-linux-gnu".
>> For bug reporting instructions, please see:
>> <http://bugs.launchpad.net/gdb-linaro/>...
>> Reading symbols from /usr/local/bin/git...done.
>> [New LWP 7174]
>>
>> warning: Can't read pathname for load map: Input/output error.
>> [Thread debugging using libthread_db enabled]
>> Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
>> Core was generated by `git branch HEAD'.
>> Program terminated with signal 11, Segmentation fault.
>> #0  cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>)
>>      at builtin/branch.c:919
>> 919                     strbuf_addf(&buf, "refs/remotes/%s", branch->name);
>> (gdb) bt
>> #0  cmd_branch (argc=1, argv=0x7fffe6e2a0f0, prefix=<optimized out>)
>>      at builtin/branch.c:919
>> #1  0x00000000004046ac in run_builtin (argv=0x7fffe6e2a0f0, argc=2,
>>      p=<optimized out>) at git.c:273
>> #2  handle_internal_command (argc=2, argv=0x7fffe6e2a0f0) at git.c:434
>> #3  0x0000000000404df3 in run_argv (argv=0x7fffe6e29f90,
>> argcp=0x7fffe6e29f9c)
>>      at git.c:480
>> #4  main (argc=2, argv=0x7fffe6e2a0f0) at git.c:555
>> (gdb) p branch
>> $1 = (struct branch *) 0x0
>> (gdb) quit
>>
>>      /ceder
>> --
>> To unsubscribe from this list: send the line "unsubscribe git" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: "git branch HEAD" dumps core when on detached head (NULL pointer dereference)
  2013-02-21 13:24   ` Per Cederqvist
@ 2013-02-21 13:32     ` Duy Nguyen
  0 siblings, 0 replies; 10+ messages in thread
From: Duy Nguyen @ 2013-02-21 13:32 UTC (permalink / raw)
  To: Per Cederqvist; +Cc: git

On Thu, Feb 21, 2013 at 8:24 PM, Per Cederqvist <cederp@opera.com> wrote:
> Sorry, but isolating the issue reporting it here is about as much time
> as I can spend on this issue. Learning the coding standard of Git and
> how to write test cases is not something I'm prepared to do, at least
> not at the moment. I hope there is a place for users and reporters of
> bugs in the Git community.

Sure. No problem. I just thought you might want to finish it off. I'll
look into it.
-- 
Duy

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH] branch: segfault fixes and validation
  2013-02-21 12:27 "git branch HEAD" dumps core when on detached head (NULL pointer dereference) Per Cederqvist
  2013-02-21 12:50 ` Duy Nguyen
@ 2013-02-21 14:18 ` Nguyễn Thái Ngọc Duy
  2013-02-21 17:47   ` Junio C Hamano
  1 sibling, 1 reply; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2013-02-21 14:18 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Per Cederqvist,
	Nguyễn Thái Ngọc Duy

branch_get() can return NULL (so far on detached HEAD only) but some
code paths in builtin/branch.c cannot deal with that and cause
segfaults. Fix it.

While at there, make sure to bail out when the user gives 2 or more
arguments, but only the first one is processed.

Reported-by: Per Cederqvist <cederp@opera.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/branch.c  | 20 ++++++++++++++++++++
 t/t3200-branch.sh | 21 +++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/builtin/branch.c b/builtin/branch.c
index 6371bf9..c1d688e 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -889,6 +889,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	} else if (new_upstream) {
 		struct branch *branch = branch_get(argv[0]);
 
+		if (argc > 1)
+			die(_("too many branches to set new upstream"));
+
+		if (!branch)
+			die(_("could not figure out the branch name from '%s'"),
+			    argc == 1 ? argv[0] : "HEAD");
+
 		if (!ref_exists(branch->refname))
 			die(_("branch '%s' does not exist"), branch->name);
 
@@ -901,6 +908,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		struct branch *branch = branch_get(argv[0]);
 		struct strbuf buf = STRBUF_INIT;
 
+		if (argc > 1)
+			die(_("too many branches to unset upstream"));
+
+		if (!branch)
+			die(_("could not figure out the branch name from '%s'"),
+			    argc == 1 ? argv[0] : "HEAD");
+
 		if (!branch_has_merge_config(branch)) {
 			die(_("Branch '%s' has no upstream information"), branch->name);
 		}
@@ -916,6 +930,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		int branch_existed = 0, remote_tracking = 0;
 		struct strbuf buf = STRBUF_INIT;
 
+		if (!strcmp(argv[0], "HEAD"))
+			die(_("it does not make sense to create 'HEAD' manually"));
+
+		if (!branch)
+			die(_("could not figure out the branch name from '%s'"), argv[0]);
+
 		if (kinds != REF_LOCAL_BRANCH)
 			die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
 
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index f3e0e4a..12f1e4a 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -42,6 +42,10 @@ test_expect_success \
     'git branch a/b/c should create a branch' \
     'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c'
 
+test_expect_success \
+    'git branch HEAD should fail' \
+    'test_must_fail git branch HEAD'
+
 cat >expect <<EOF
 $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
 EOF
@@ -388,6 +392,14 @@ test_expect_success \
     'git tag foobar &&
      test_must_fail git branch --track my11 foobar'
 
+test_expect_success '--set-upstream-to fails on multiple branches' \
+    'test_must_fail git branch --set-upstream-to master a b c'
+
+test_expect_success '--set-upstream-to fails on detached HEAD' \
+    'git checkout HEAD^{} &&
+     test_must_fail git branch --set-upstream-to master &&
+     git checkout -'
+
 test_expect_success 'use --set-upstream-to modify HEAD' \
     'test_config branch.master.remote foo &&
      test_config branch.master.merge foo &&
@@ -417,6 +429,15 @@ test_expect_success 'test --unset-upstream on HEAD' \
      test_must_fail git branch --unset-upstream
 '
 
+test_expect_success '--unset-upstream should fail on multiple branches' \
+    'test_must_fail git branch --unset-upstream a b c'
+
+test_expect_success '--unset-upstream should fail on detached HEAD' \
+    'git checkout HEAD^{} &&
+     test_must_fail git branch --unset-upstream &&
+     git checkout -
+'
+
 test_expect_success 'test --unset-upstream on a particular branch' \
     'git branch my15
      git branch --set-upstream-to master my14 &&
-- 
1.8.1.2.536.gf441e6d

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: segfault fixes and validation
  2013-02-21 14:18 ` [PATCH] branch: segfault fixes and validation Nguyễn Thái Ngọc Duy
@ 2013-02-21 17:47   ` Junio C Hamano
  2013-02-22 11:47     ` [PATCH v2] " Nguyễn Thái Ngọc Duy
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2013-02-21 17:47 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Per Cederqvist

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> branch_get() can return NULL (so far on detached HEAD only)...

Do you anticipate any other cases where the API call should validly
return NULL?  I offhand do not, ...

> but some
> code paths in builtin/branch.c cannot deal with that and cause
> segfaults. Fix it.
>
> While at there, make sure to bail out when the user gives 2 or more
> arguments, but only the first one is processed.
>
> Reported-by: Per Cederqvist <cederp@opera.com>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  builtin/branch.c  | 20 ++++++++++++++++++++
>  t/t3200-branch.sh | 21 +++++++++++++++++++++
>  2 files changed, 41 insertions(+)
>
> diff --git a/builtin/branch.c b/builtin/branch.c
> index 6371bf9..c1d688e 100644
> --- a/builtin/branch.c
> +++ b/builtin/branch.c
> @@ -889,6 +889,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
>  	} else if (new_upstream) {
>  		struct branch *branch = branch_get(argv[0]);
>  
> +		if (argc > 1)
> +			die(_("too many branches to set new upstream"));
> +
> +		if (!branch)
> +			die(_("could not figure out the branch name from '%s'"),
> +			    argc == 1 ? argv[0] : "HEAD");

... and find this "could not figure out" very unfriendly to the
user.  Is it a bug in the implementation, silly Git failing to find
what branch the user meant?  What recourse does the user have at
this point?

Or is it a user error to ask Git to operate on the branch pointed at
by HEAD, when HEAD does not refer to any branch?  If that is the
case, then the message should say that there is no current branch to
operate on because the user is on a detached HEAD.  That would point
the user in the right direction to correct the user error, no?

Of course, argv[0] may not be HEAD and in that case you may have to
say "no such branch %s" % argv[0] or something.  The point is that
"could not figure out" feels a bit too lazy.

> @@ -901,6 +908,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
>  		struct branch *branch = branch_get(argv[0]);
>  		struct strbuf buf = STRBUF_INIT;
>  
> +		if (argc > 1)
> +			die(_("too many branches to unset upstream"));
> +
> +		if (!branch)
> +			die(_("could not figure out the branch name from '%s'"),
> +			    argc == 1 ? argv[0] : "HEAD");

Likewise.

> @@ -916,6 +930,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
>  		int branch_existed = 0, remote_tracking = 0;
>  		struct strbuf buf = STRBUF_INIT;
>  
> +		if (!strcmp(argv[0], "HEAD"))
> +			die(_("it does not make sense to create 'HEAD' manually"));
> +
> +		if (!branch)
> +			die(_("could not figure out the branch name from '%s'"), argv[0]);

Likewise.

Thanks.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2] branch: segfault fixes and validation
  2013-02-21 17:47   ` Junio C Hamano
@ 2013-02-22 11:47     ` Nguyễn Thái Ngọc Duy
  2013-02-22 20:27       ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2013-02-22 11:47 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Per Cederqvist,
	Nguyễn Thái Ngọc Duy

branch_get() can return NULL (so far on detached HEAD only) but some
code paths in builtin/branch.c cannot deal with that and cause
segfaults. While at there, make sure we bail out when the user gives 2
or more arguments, but we only process the first one and silently
ignore the rest.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 On Fri, Feb 22, 2013 at 12:47 AM, Junio C Hamano <gitster@pobox.com> wrote:
 > Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
 >
 >> branch_get() can return NULL (so far on detached HEAD only)...
 >
 > Do you anticipate any other cases where the API call should validly
 > return NULL?
 
 No. But I do not see any guarantee that it may never do that in
 future either. Which is why I was deliberately vague with "could not
 figure out...". But you also correctly observed my laziness there. So
 how about this? It makes a special case for HEAD but not insist on
 detached HEAD as the only cause.

 builtin/branch.c  | 24 ++++++++++++++++++++++++
 t/t3200-branch.sh | 21 +++++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/builtin/branch.c b/builtin/branch.c
index 6371bf9..82ed337 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -889,6 +889,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	} else if (new_upstream) {
 		struct branch *branch = branch_get(argv[0]);
 
+		if (argc > 1)
+			die(_("too many branches to set new upstream"));
+
+		if (!branch) {
+			if (!argc || !strcmp(argv[0], "HEAD"))
+				die(_("HEAD does not point to any branch. Is it detached?"));
+			die(_("no such branch '%s'"), argv[0]);
+		}
+
 		if (!ref_exists(branch->refname))
 			die(_("branch '%s' does not exist"), branch->name);
 
@@ -901,6 +910,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		struct branch *branch = branch_get(argv[0]);
 		struct strbuf buf = STRBUF_INIT;
 
+		if (argc > 1)
+			die(_("too many branches to unset upstream"));
+
+		if (!branch) {
+			if (!argc || !strcmp(argv[0], "HEAD"))
+				die(_("HEAD does not point to any branch. Is it detached?"));
+			die(_("no such branch '%s'"), argv[0]);
+		}
+
 		if (!branch_has_merge_config(branch)) {
 			die(_("Branch '%s' has no upstream information"), branch->name);
 		}
@@ -916,6 +934,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		int branch_existed = 0, remote_tracking = 0;
 		struct strbuf buf = STRBUF_INIT;
 
+		if (!strcmp(argv[0], "HEAD"))
+			die(_("it does not make sense to create 'HEAD' manually"));
+
+		if (!branch)
+			die(_("no such branch '%s'"), argv[0]);
+
 		if (kinds != REF_LOCAL_BRANCH)
 			die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
 
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index f3e0e4a..12f1e4a 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -42,6 +42,10 @@ test_expect_success \
     'git branch a/b/c should create a branch' \
     'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c'
 
+test_expect_success \
+    'git branch HEAD should fail' \
+    'test_must_fail git branch HEAD'
+
 cat >expect <<EOF
 $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
 EOF
@@ -388,6 +392,14 @@ test_expect_success \
     'git tag foobar &&
      test_must_fail git branch --track my11 foobar'
 
+test_expect_success '--set-upstream-to fails on multiple branches' \
+    'test_must_fail git branch --set-upstream-to master a b c'
+
+test_expect_success '--set-upstream-to fails on detached HEAD' \
+    'git checkout HEAD^{} &&
+     test_must_fail git branch --set-upstream-to master &&
+     git checkout -'
+
 test_expect_success 'use --set-upstream-to modify HEAD' \
     'test_config branch.master.remote foo &&
      test_config branch.master.merge foo &&
@@ -417,6 +429,15 @@ test_expect_success 'test --unset-upstream on HEAD' \
      test_must_fail git branch --unset-upstream
 '
 
+test_expect_success '--unset-upstream should fail on multiple branches' \
+    'test_must_fail git branch --unset-upstream a b c'
+
+test_expect_success '--unset-upstream should fail on detached HEAD' \
+    'git checkout HEAD^{} &&
+     test_must_fail git branch --unset-upstream &&
+     git checkout -
+'
+
 test_expect_success 'test --unset-upstream on a particular branch' \
     'git branch my15
      git branch --set-upstream-to master my14 &&
-- 
1.8.1.2.536.gf441e6d

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v2] branch: segfault fixes and validation
  2013-02-22 11:47     ` [PATCH v2] " Nguyễn Thái Ngọc Duy
@ 2013-02-22 20:27       ` Junio C Hamano
  2013-02-23 12:22         ` [PATCH v3] " Nguyễn Thái Ngọc Duy
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2013-02-22 20:27 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Per Cederqvist

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

> branch_get() can return NULL (so far on detached HEAD only) but some
> code paths in builtin/branch.c cannot deal with that and cause
> segfaults. While at there, make sure we bail out when the user gives 2
> or more arguments, but we only process the first one and silently
> ignore the rest.

Explain "2 or more arguments" in what context, perhaps?  Otherwise
it makes it sound as if "git branch foo bar baz" is covered with
this patch, no?

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  On Fri, Feb 22, 2013 at 12:47 AM, Junio C Hamano <gitster@pobox.com> wrote:
>  > Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>  >
>  >> branch_get() can return NULL (so far on detached HEAD only)...
>  >
>  > Do you anticipate any other cases where the API call should validly
>  > return NULL?
>  
>  No. But I do not see any guarantee that it may never do that in
>  future either. Which is why I was deliberately vague with "could not
>  figure out...". But you also correctly observed my laziness there. So
>  how about this? It makes a special case for HEAD but not insist on
>  detached HEAD as the only cause.

Sure.  It looks better.

What you can do is to have a single helper function that can explain
why branch_get() returned NULL (or extend branch_get() to serve that
purpose as well); then you do not have to duplicate the logic twice
on the caller's side (and there may be other callers that want to do
the same).

> diff --git a/builtin/branch.c b/builtin/branch.c
> index 6371bf9..82ed337 100644
> --- a/builtin/branch.c
> +++ b/builtin/branch.c
> @@ -889,6 +889,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
>  	} else if (new_upstream) {
>  		struct branch *branch = branch_get(argv[0]);
>  
> +		if (argc > 1)
> +			die(_("too many branches to set new upstream"));
> +
> +		if (!branch) {
> +			if (!argc || !strcmp(argv[0], "HEAD"))
> +				die(_("HEAD does not point to any branch. Is it detached?"));
> +			die(_("no such branch '%s'"), argv[0]);
> +		}
> +
>  		if (!ref_exists(branch->refname))
>  			die(_("branch '%s' does not exist"), branch->name);

The latter part of the new code triggers when branch_get() returns
NULL while doing "git branch --set-upstream-to=X [Y]".  When "Y" is
string "HEAD" or missing, the first die() is triggered and says a
funny thing. If HEAD does not point to any branch, by definition it
is detached.  The user may say "Yes, I know it is detached." but the
message does not help the user to figure out what to do next.

Instead of asking "Is it detached?", perhaps we can say something
like "You told me to set the upstream of HEAD to branch X, but " in
front?  At least, that will be a better explanation for the reason
why the operation is failing.

The existing test might be wrong, by the way.  Your HEAD may point
at a branch Y but you may not have any commit on it yet, and you may
want to allow setting the upstream of that to-be-born branch to
another branch X with "branch --set-upstream-to=X [Y|HEAD]".

While I think it is insane to do anything before creating the first
commit on your current branch (or using "checkout --orphan" in
general) and it may not be worth our time to babysit users who do
so, but the following sequence may feel natural to them:

	git checkout --orphan X
        git branch --set-upstream-to=master

	... perhaps create an initial commit, perhaps not ...

	git merge @{upstream}

For that to work sanely, perhaps the pattern

	branch = branch_get();
        if (!branch)
		die due to no branch;
	if (!ref_exists(branch->refname))
		die due to typo in branch name

may need to be fixed globally, replacing ref_exists(branch->refname)
with branch_exists(branch) that returns true if branch->refname is
an existing ref, or the branch in question was obtained by checking
with current_branch (in remote.c), or something like that.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3] branch: segfault fixes and validation
  2013-02-22 20:27       ` Junio C Hamano
@ 2013-02-23 12:22         ` Nguyễn Thái Ngọc Duy
  2013-02-23 20:01           ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2013-02-23 12:22 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Per Cederqvist,
	Nguyễn Thái Ngọc Duy

branch_get() can return NULL (so far on detached HEAD only) but some
code paths in builtin/branch.c cannot deal with that and cause
segfaults.

While at there, make sure to bail out when the user gives 2 or more
branches with --set-upstream-to or --unset-upstream, where only the
first branch is processed and the rest silently dropped.

Reported-by: Per Cederqvist <cederp@opera.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 On Sat, Feb 23, 2013 at 3:27 AM, Junio C Hamano <gitster@pobox.com> wrote:
 > Instead of asking "Is it detached?", perhaps we can say something
 > like "You told me to set the upstream of HEAD to branch X, but " in
 > front?  At least, that will be a better explanation for the reason
 > why the operation is failing.

 Fixed.

 > What you can do is to have a single helper function that can explain
 > why branch_get() returned NULL (or extend branch_get() to serve that
 > purpose as well); then you do not have to duplicate the logic twice
 > on the caller's side (and there may be other callers that want to do
 > the same).

 The explanation mentions about the failed operation, which makes a
 helper less useful. We could still do the helper, but it may lead to
 i18n legos. So no helper in this version.

 > The existing test might be wrong, by the way.  Your HEAD may point
 > at a branch Y but you may not have any commit on it yet, and you may
 > want to allow setting the upstream of that to-be-born branch to
 > another branch X with "branch --set-upstream-to=X [Y|HEAD]".

 It sounds complicated. I think we can revisit it when a user actually
 complains about it.

 builtin/branch.c  | 27 +++++++++++++++++++++++++++
 t/t3200-branch.sh | 21 +++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/builtin/branch.c b/builtin/branch.c
index 6371bf9..00d17d2 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -889,6 +889,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	} else if (new_upstream) {
 		struct branch *branch = branch_get(argv[0]);
 
+		if (argc > 1)
+			die(_("too many branches to set new upstream"));
+
+		if (!branch) {
+			if (!argc || !strcmp(argv[0], "HEAD"))
+				die(_("could not set upstream of HEAD to %s when "
+				      "it does not point to any branch."),
+				    new_upstream);
+			die(_("no such branch '%s'"), argv[0]);
+		}
+
 		if (!ref_exists(branch->refname))
 			die(_("branch '%s' does not exist"), branch->name);
 
@@ -901,6 +912,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		struct branch *branch = branch_get(argv[0]);
 		struct strbuf buf = STRBUF_INIT;
 
+		if (argc > 1)
+			die(_("too many branches to unset upstream"));
+
+		if (!branch) {
+			if (!argc || !strcmp(argv[0], "HEAD"))
+				die(_("could not unset upstream of HEAD when "
+				      "it does not point to any branch."));
+			die(_("no such branch '%s'"), argv[0]);
+		}
+
 		if (!branch_has_merge_config(branch)) {
 			die(_("Branch '%s' has no upstream information"), branch->name);
 		}
@@ -916,6 +937,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		int branch_existed = 0, remote_tracking = 0;
 		struct strbuf buf = STRBUF_INIT;
 
+		if (!strcmp(argv[0], "HEAD"))
+			die(_("it does not make sense to create 'HEAD' manually"));
+
+		if (!branch)
+			die(_("no such branch '%s'"), argv[0]);
+
 		if (kinds != REF_LOCAL_BRANCH)
 			die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
 
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index f3e0e4a..12f1e4a 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -42,6 +42,10 @@ test_expect_success \
     'git branch a/b/c should create a branch' \
     'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c'
 
+test_expect_success \
+    'git branch HEAD should fail' \
+    'test_must_fail git branch HEAD'
+
 cat >expect <<EOF
 $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	branch: Created from master
 EOF
@@ -388,6 +392,14 @@ test_expect_success \
     'git tag foobar &&
      test_must_fail git branch --track my11 foobar'
 
+test_expect_success '--set-upstream-to fails on multiple branches' \
+    'test_must_fail git branch --set-upstream-to master a b c'
+
+test_expect_success '--set-upstream-to fails on detached HEAD' \
+    'git checkout HEAD^{} &&
+     test_must_fail git branch --set-upstream-to master &&
+     git checkout -'
+
 test_expect_success 'use --set-upstream-to modify HEAD' \
     'test_config branch.master.remote foo &&
      test_config branch.master.merge foo &&
@@ -417,6 +429,15 @@ test_expect_success 'test --unset-upstream on HEAD' \
      test_must_fail git branch --unset-upstream
 '
 
+test_expect_success '--unset-upstream should fail on multiple branches' \
+    'test_must_fail git branch --unset-upstream a b c'
+
+test_expect_success '--unset-upstream should fail on detached HEAD' \
+    'git checkout HEAD^{} &&
+     test_must_fail git branch --unset-upstream &&
+     git checkout -
+'
+
 test_expect_success 'test --unset-upstream on a particular branch' \
     'git branch my15
      git branch --set-upstream-to master my14 &&
-- 
1.8.1.2.536.gf441e6d

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] branch: segfault fixes and validation
  2013-02-23 12:22         ` [PATCH v3] " Nguyễn Thái Ngọc Duy
@ 2013-02-23 20:01           ` Junio C Hamano
  0 siblings, 0 replies; 10+ messages in thread
From: Junio C Hamano @ 2013-02-23 20:01 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Per Cederqvist

Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:

>  > What you can do is to have a single helper function that can explain
>  > why branch_get() returned NULL (or extend branch_get() to serve that
>  > purpose as well); then you do not have to duplicate the logic twice
>  > on the caller's side (and there may be other callers that want to do
>  > the same).
>
>  The explanation mentions about the failed operation, which makes a
>  helper less useful. We could still do the helper, but it may lead to
>  i18n legos. So no helper in this version.

Who said explanation has to be conveyed in human language to the
caller of the helper?  Since there are only two possible cases, at
least for now, and I do not think there will be countless many in
the future, for the branch_get() function to fail, you can still do:

    	int explain_error;
	struct branch *branch = branch_get(argv[0], &explain_error);

        switch (explain_error) {
	case DEFAULT_HEAD_DETACHED:
        case GIVEN_HEAD_DETACHED:
		die(_("could not make %s upstream of the current branch "
			"because you do not have one"), new_upstream);
		break;
	default:
		break;
        }

and we could even fold the !ref_exists() check that is there for
typo-detection purposes into the framework, e.g.

	case GIVEN_BRANCH_DOES_NOT_EXIST:
		die(_("you told me to make %s upstream of %s "
			"but the latter does not exist yet"),
			new_upstream, argv[0]);

if we wanted to preserve what the current test does, no?

>  > The existing test might be wrong, by the way.  Your HEAD may point
>  > at a branch Y but you may not have any commit on it yet, and you may
>  > want to allow setting the upstream of that to-be-born branch to
>  > another branch X with "branch --set-upstream-to=X [Y|HEAD]".
>
>  It sounds complicated. I think we can revisit it when a user actually
>  complains about it.

Yeah, will replace the previous one and queue this version.

Thanks.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2013-02-23 20:02 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-21 12:27 "git branch HEAD" dumps core when on detached head (NULL pointer dereference) Per Cederqvist
2013-02-21 12:50 ` Duy Nguyen
2013-02-21 13:24   ` Per Cederqvist
2013-02-21 13:32     ` Duy Nguyen
2013-02-21 14:18 ` [PATCH] branch: segfault fixes and validation Nguyễn Thái Ngọc Duy
2013-02-21 17:47   ` Junio C Hamano
2013-02-22 11:47     ` [PATCH v2] " Nguyễn Thái Ngọc Duy
2013-02-22 20:27       ` Junio C Hamano
2013-02-23 12:22         ` [PATCH v3] " Nguyễn Thái Ngọc Duy
2013-02-23 20:01           ` 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).