All of lore.kernel.org
 help / color / mirror / Atom feed
* [BUG] typo DWIMery with alias broken (cd to random dir)
@ 2016-01-26  7:37 Michael J Gruber
  2016-01-26 12:50 ` Duy Nguyen
  2016-01-26 13:26 ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Nguyễn Thái Ngọc Duy
  0 siblings, 2 replies; 22+ messages in thread
From: Michael J Gruber @ 2016-01-26  7:37 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Nguyen Thai Ngoc Duy

Hi there,

with current next (989ee58 plus local additions) it seems that typo
DWIMery with aliases is broken, see below.

It appears that the typo DWIMery is broken only when there is a unique
automatic DWIM substitution for a mistyped alias.

I haven't bisected yet, but I suspect this to be related to recent
changes regarding the environment in which commands/aliases are started
(though this happens without extra work trees), so I'm cc'ing an expert
in that area. Funny, though, that my user name shows up...

I think the reason is that git.c's handle_alias() (or something else)
calls restore_env() multiple times, and restore_env frees orig_cwd such
that subsequent restore_env(0) with external_alias=0 tries to cd to a
random location.

I have no idea whether orig_cwd=0 after freeing or something else would
be the proper fix.

Michael

LANG=C git sss
WARNING: You called a Git command named 'sss', which does not exist.
Continuing under the assumption that you meant 'ss'
in 2.0 seconds automatically...
fatal: could not move to git@drmicha.warpmail.net: No such file or directory
[mjg@skimbleshanks git]✗ LANG=C git ss
## HEAD (no branch)
?? a
?? a.patch
?? c2d.sh
[mjg@skimbleshanks git]✓ LANG=C git statu -sb
git: 'statu' is not a git command. See 'git --help'.

Did you mean one of these?
        status
        stage
        stash
[mjg@skimbleshanks git]✗ LANG=C git statuss -sb
WARNING: You called a Git command named 'statuss', which does not exist.
Continuing under the assumption that you meant 'status'
in 2.0 seconds automatically...
## HEAD (no branch)
?? a
?? a.patch
?? c2d.sh

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

* Re: [BUG] typo DWIMery with alias broken (cd to random dir)
  2016-01-26  7:37 [BUG] typo DWIMery with alias broken (cd to random dir) Michael J Gruber
@ 2016-01-26 12:50 ` Duy Nguyen
  2016-01-26 13:26 ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Nguyễn Thái Ngọc Duy
  1 sibling, 0 replies; 22+ messages in thread
From: Duy Nguyen @ 2016-01-26 12:50 UTC (permalink / raw)
  To: Michael J Gruber; +Cc: Git Mailing List

On Tue, Jan 26, 2016 at 2:37 PM, Michael J Gruber
<git@drmicha.warpmail.net> wrote:
> Hi there,
>
> with current next (989ee58 plus local additions) it seems that typo
> DWIMery with aliases is broken, see below.
>
> It appears that the typo DWIMery is broken only when there is a unique
> automatic DWIM substitution for a mistyped alias.
>
> I haven't bisected yet, but I suspect this to be related to recent
> changes regarding the environment in which commands/aliases are started
> (though this happens without extra work trees), so I'm cc'ing an expert
> in that area. Funny, though, that my user name shows up...
>
> I think the reason is that git.c's handle_alias() (or something else)
> calls restore_env() multiple times, and restore_env frees orig_cwd such
> that subsequent restore_env(0) with external_alias=0 tries to cd to a
> random location.
>
> I have no idea whether orig_cwd=0 after freeing or something else would
> be the proper fix.

I think the key is to reset saved_env_before_alias to zero in
restore_env(). With that flag remains set, the second save_env turns
no-op. orig_cwd should be reset too, but that's minor. It's working
for me. I'll send a patch later.
-- 
Duy

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

* [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it
  2016-01-26  7:37 [BUG] typo DWIMery with alias broken (cd to random dir) Michael J Gruber
  2016-01-26 12:50 ` Duy Nguyen
@ 2016-01-26 13:26 ` Nguyễn Thái Ngọc Duy
  2016-01-26 14:39   ` Michael J Gruber
                     ` (2 more replies)
  1 sibling, 3 replies; 22+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2016-01-26 13:26 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, git, Nguyễn Thái Ngọc Duy

Commit 57ea712 (git.c: make sure we do not leak GIT_* to alias scripts -
2015-12-20) does not realize that handle_alias() can be called multiple
times because of the forever loop in run_argv(). The commit breaks alias
chains.

Suppose you have an alias "abc" that resolves to another alias "def",
which finally resolve to "git status". handle_alias() is called twice.
save_env() and restore_env() are also called twice. But because of the
check save_env_before_alias in save_env(), we save once while trying to
restore twice. Consequences are left for the reader's imagination.

Fortunately, you cannot make an alias of another alias. At least not
yet. Unfortunately it can still happen with help.autocorrect, where your
alias typo is treated as the first "alias", and it can be resolved to
the second alias. Then boom.

Make sure we call save_env() and restore_env() in pairs. While at there,
set orig_cwd to NULL after freeing it for hygiene.

Reported-by: Michael J Gruber <git@drmicha.warpmail.net>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 git.c                  | 11 ++++++++---
 t/t1300-repo-config.sh |  8 ++++++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/git.c b/git.c
index da278c3..cd733f7 100644
--- a/git.c
+++ b/git.c
@@ -25,14 +25,15 @@ static const char *env_names[] = {
 	GIT_PREFIX_ENVIRONMENT
 };
 static char *orig_env[4];
-static int saved_env_before_alias;
+static int saved_env_before_alias, saved;
 
 static void save_env_before_alias(void)
 {
 	int i;
-	if (saved_env_before_alias)
-		return;
+	if (saved)
+		die("BUG: uneven pair of save_env/restore_env calls");
 	saved_env_before_alias = 1;
+	saved = 1;
 	orig_cwd = xgetcwd();
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 		orig_env[i] = getenv(env_names[i]);
@@ -44,9 +45,13 @@ static void save_env_before_alias(void)
 static void restore_env(int external_alias)
 {
 	int i;
+	if (saved != 1)
+		die("BUG: uneven pair of save_env/restore_env calls");
 	if (!external_alias && orig_cwd && chdir(orig_cwd))
 		die_errno("could not move to %s", orig_cwd);
 	free(orig_cwd);
+	orig_cwd = NULL;
+	saved = 0;
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 		if (external_alias &&
 		    !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 52678e7..3f95285 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -1201,4 +1201,12 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
 	  "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
 '
 
+test_expect_success 'autocorrect and save_env/restore_env' '
+	git config alias.ss status &&
+	git config help.autocorrect 1 &&
+	git sss --porcelain | grep actual >actual &&
+	echo "?? actual" >expected &&
+	test_cmp expected actual
+'
+
 test_done
-- 
2.7.0.288.g1d8ad15

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

* Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it
  2016-01-26 13:26 ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Nguyễn Thái Ngọc Duy
@ 2016-01-26 14:39   ` Michael J Gruber
  2016-01-26 17:44     ` Junio C Hamano
  2016-01-26 19:02   ` Junio C Hamano
  2016-01-26 20:11   ` Junio C Hamano
  2 siblings, 1 reply; 22+ messages in thread
From: Michael J Gruber @ 2016-01-26 14:39 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy, git; +Cc: Junio C Hamano

Nguyễn Thái Ngọc Duy venit, vidit, dixit 26.01.2016 14:26:
> Commit 57ea712 (git.c: make sure we do not leak GIT_* to alias scripts -
> 2015-12-20) does not realize that handle_alias() can be called multiple
> times because of the forever loop in run_argv(). The commit breaks alias
> chains.
> 
> Suppose you have an alias "abc" that resolves to another alias "def",
> which finally resolve to "git status". handle_alias() is called twice.
> save_env() and restore_env() are also called twice. But because of the
> check save_env_before_alias in save_env(), we save once while trying to
> restore twice. Consequences are left for the reader's imagination.
> 
> Fortunately, you cannot make an alias of another alias. At least not
> yet. Unfortunately it can still happen with help.autocorrect, where your
> alias typo is treated as the first "alias", and it can be resolved to
> the second alias. Then boom.
> 
> Make sure we call save_env() and restore_env() in pairs. While at there,
> set orig_cwd to NULL after freeing it for hygiene.
> 
> Reported-by: Michael J Gruber <git@drmicha.warpmail.net>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---

The patch fixes it for me, thanks!

Michael

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

* Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it
  2016-01-26 14:39   ` Michael J Gruber
@ 2016-01-26 17:44     ` Junio C Hamano
  0 siblings, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-01-26 17:44 UTC (permalink / raw)
  To: Michael J Gruber; +Cc: Nguyễn Thái Ngọc Duy, git

Michael J Gruber <git@drmicha.warpmail.net> writes:

> Nguyễn Thái Ngọc Duy venit, vidit, dixit 26.01.2016 14:26:
>> Commit 57ea712 (git.c: make sure we do not leak GIT_* to alias scripts -
>> 2015-12-20) does not realize that handle_alias() can be called multiple
>> times because of the forever loop in run_argv(). The commit breaks alias
>> chains.
>> 
>> Suppose you have an alias "abc" that resolves to another alias "def",
>> which finally resolve to "git status". handle_alias() is called twice.
>> save_env() and restore_env() are also called twice. But because of the
>> check save_env_before_alias in save_env(), we save once while trying to
>> restore twice. Consequences are left for the reader's imagination.
>> 
>> Fortunately, you cannot make an alias of another alias. At least not
>> yet. Unfortunately it can still happen with help.autocorrect, where your
>> alias typo is treated as the first "alias", and it can be resolved to
>> the second alias. Then boom.
>> 
>> Make sure we call save_env() and restore_env() in pairs. While at there,
>> set orig_cwd to NULL after freeing it for hygiene.
>> 
>> Reported-by: Michael J Gruber <git@drmicha.warpmail.net>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>
> The patch fixes it for me, thanks!
>
> Michael

Thanks for noticing, and thanks for describing the problem very
clearly and fixing it quickly.

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

* Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it
  2016-01-26 13:26 ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Nguyễn Thái Ngọc Duy
  2016-01-26 14:39   ` Michael J Gruber
@ 2016-01-26 19:02   ` Junio C Hamano
  2016-01-26 20:11   ` Junio C Hamano
  2 siblings, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-01-26 19:02 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, git

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

> diff --git a/git.c b/git.c
> index da278c3..cd733f7 100644
> --- a/git.c
> +++ b/git.c
> @@ -25,14 +25,15 @@ static const char *env_names[] = {
>  	GIT_PREFIX_ENVIRONMENT
>  };
>  static char *orig_env[4];
> -static int saved_env_before_alias;
> +static int saved_env_before_alias, saved;

Even for a file-local static, this name is a bit too generic, isn't
it?  saved_env_count or something perhaps?


>  static void save_env_before_alias(void)
>  {
>  	int i;
> -	if (saved_env_before_alias)
> -		return;
> +	if (saved)
> +		die("BUG: uneven pair of save_env/restore_env calls");
>  	saved_env_before_alias = 1;
> +	saved = 1;
>  	orig_cwd = xgetcwd();
>  	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
>  		orig_env[i] = getenv(env_names[i]);
> @@ -44,9 +45,13 @@ static void save_env_before_alias(void)
>  static void restore_env(int external_alias)
>  {
>  	int i;
> +	if (saved != 1)
> +		die("BUG: uneven pair of save_env/restore_env calls");
>  	if (!external_alias && orig_cwd && chdir(orig_cwd))
>  		die_errno("could not move to %s", orig_cwd);
>  	free(orig_cwd);
> +	orig_cwd = NULL;
> +	saved = 0;
>  	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
>  		if (external_alias &&
>  		    !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
> diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
> index 52678e7..3f95285 100755
> --- a/t/t1300-repo-config.sh
> +++ b/t/t1300-repo-config.sh
> @@ -1201,4 +1201,12 @@ test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
>  	  "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
>  '
>  
> +test_expect_success 'autocorrect and save_env/restore_env' '
> +	git config alias.ss status &&
> +	git config help.autocorrect 1 &&
> +	git sss --porcelain | grep actual >actual &&
> +	echo "?? actual" >expected &&
> +	test_cmp expected actual
> +'
> +
>  test_done

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

* Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it
  2016-01-26 13:26 ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Nguyễn Thái Ngọc Duy
  2016-01-26 14:39   ` Michael J Gruber
  2016-01-26 19:02   ` Junio C Hamano
@ 2016-01-26 20:11   ` Junio C Hamano
  2016-01-27  6:49     ` Junio C Hamano
  2 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2016-01-26 20:11 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, git

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

> Commit 57ea712 (git.c: make sure we do not leak GIT_* to alias scripts -
> 2015-12-20) does not realize that handle_alias() can be called multiple
> times because of the forever loop in run_argv(). The commit breaks alias
> chains.
>
> Suppose you have an alias "abc" that resolves to another alias "def",
> which finally resolve to "git status". handle_alias() is called twice.
> save_env() and restore_env() are also called twice. But because of the
> check save_env_before_alias in save_env(), we save once while trying to
> restore twice. Consequences are left for the reader's imagination.
>
> Fortunately, you cannot make an alias of another alias. At least not
> yet. Unfortunately it can still happen with help.autocorrect, where your
> alias typo is treated as the first "alias", and it can be resolved to
> the second alias. Then boom.
>
> Make sure we call save_env() and restore_env() in pairs. While at there,
> set orig_cwd to NULL after freeing it for hygiene.
>
> Reported-by: Michael J Gruber <git@drmicha.warpmail.net>
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---

I spoke too soon, I am afraid.

The log message talks about "we save once while trying to restore
twice" being bad, and the fix is to make sure we save and restore in
pairs.  Before or after the patch, however, there is no change in
the callers of save_env_before_alias() and restore_env(), and it is
unclear how that is ensured.

It turns out that the callers are doing the right thing (assuming
that calling save and restore in pairs is the right solution), and
the culprit is in the save_env_before_alias() function that returns
without saving when we were called.  That is mentioned in the log,
but the description of last paragraph leaves the impression that it
was callers that needed fixing, which is misleading.

After the patch, we have "saved" and "saved_env_before_alias".

 - The former is a protection against committing similar programming
   errors in the future (i.e. glorified assert()).  Can we have it
   in a separate commit, perhaps on top of the real fix, to make the
   change for the real fix easier to understand?

 - Do we still need the latter?  saved_env_before_alias is set to 1
   if we called save_env_before_alias() even once, which happens
   always (and only) at the beginning of handle_alias(), so that is
   equivalent to saying "have we ever called handle_alias()?"

   And there is only one caller of handle_alias, in run_argv(), and
   it maintains yet another variable "done_alias" to keep track of
   the same information.

   The only code that cares about saved_env_before_alias is
   handle_builtin(), but it is a glorified no-op if
   saved_env_before_alias() is set.  And the only caller of this
   handle_builtin(), for which saved_env_before_alias matters, is
   the same run_argv().

I wonder if this would be better done as a multi-part series that
goes like this:

 [1/3] git: remove an early return from save_env_before_alias()

       whose description would be the first two paragraphs of your
       patch, with two line removal, i.e.

        diff --git a/git.c b/git.c
        index 98d4412..a57a4cb 100644
        --- a/git.c
        +++ b/git.c
        @@ -30,8 +30,6 @@ static int saved_env_before_alias;
         static void save_env_before_alias(void)
         {
                int i;
        -	if (saved_env_before_alias)
        -		return;
                saved_env_before_alias = 1;
                orig_cwd = xgetcwd();
                for (i = 0; i < ARRAY_SIZE(env_names); i++) {

       that should be a sufficient "fix" to the issue.

 [2/3] git: protect against unbalanced calls to {save,restore}_env()

       We made sure that save_env_before_alias() does not skip
       saving the environment when asked to (which led to double
       free of orig_cwd in restore_env()) with the previous step.
       Protect against future breakage where somebody adds new
       callers of these functions in an unbalanced fashion.
       
        diff --git a/git.c b/git.c
        index a57a4cb..e39b972 100644
        --- a/git.c
        +++ b/git.c
        @@ -26,11 +26,15 @@ static const char *env_names[] = {
         };
         static char *orig_env[4];
         static int saved_env_before_alias;
        +static int save_restore_env_balance;

         static void save_env_before_alias(void)
         {
                int i;
                saved_env_before_alias = 1;
        +
        +	assert(save_restore_env_balance == 0);
        +	save_restore_env_balance = 1;
                orig_cwd = xgetcwd();
                for (i = 0; i < ARRAY_SIZE(env_names); i++) {
                        orig_env[i] = getenv(env_names[i]);
        @@ -42,6 +46,9 @@ static void save_env_before_alias(void)
         static void restore_env(int external_alias)
         {
                int i;
        +
        +	assert(save_restore_env_balance == 1);
        +	save_restore_env_balance = 0;
                if (!external_alias && orig_cwd && chdir(orig_cwd))
                        die_errno("could not move to %s", orig_cwd);
                free(orig_cwd);

 [3/3] git: simplify environment save/restore logic

       The only code that cares about the value of the global
       variable saved_env_before_alias after the previous fix is
       handle_builtin() that turns into a glorified no-op when the
       variable is true, so the logic could safely be lifted to its
       caller, i.e. the caller can refrain from calling it when the
       variable is set.

       This variable tells us if save_env_before_alias() was called
       (with or without matching restore_env()), but the sole caller
       of the function, handle_alias(), always calls it the first as
       thing, so it essentially keeps track of the fact that
       handle_alias() has ever been called.

       It turns out that handle_builtin() and handle_alias() are
       called only from one function in a way that the value of the
       variable matters, which is run_argv(), and it already keeps
       track of the fact that it called handle_alias().

       So we can simplify the whole thing by:

       - Change handle_builtin() to always make a direct call to the
         builtin implementation it finds, and make sure the caller
         refrains from calling it if handle_alias() has ever been
         called;

       - Remove saved_env_before_alias variable, and instead use the
         local "done_alias" variable maintained inside run_argv() to
         make the above decision.

 [4/3] git: plug leaks of saved environments

       I think orig_env[] members if save_env() is called twice are
       leaked, so they would need to be free'd just like orig_cwd is
       freed in restore_env().  Optionally, you can assign NULL to
       them and also to orig_cwd after freeing.



The patch text for [3/3] would look like this.

 git.c | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/git.c b/git.c
index e39b972..c8d7b56 100644
--- a/git.c
+++ b/git.c
@@ -25,13 +25,11 @@ static const char *env_names[] = {
 	GIT_PREFIX_ENVIRONMENT
 };
 static char *orig_env[4];
-static int saved_env_before_alias;
 static int save_restore_env_balance;
 
 static void save_env_before_alias(void)
 {
 	int i;
-	saved_env_before_alias = 1;
 
 	assert(save_restore_env_balance == 0);
 	save_restore_env_balance = 1;
@@ -533,16 +531,8 @@ static void handle_builtin(int argc, const char **argv)
 	}
 
 	builtin = get_builtin(cmd);
-	if (builtin) {
-		/*
-		 * XXX: if we can figure out cases where it is _safe_
-		 * to do, we can avoid spawning a new process when
-		 * saved_env_before_alias is true
-		 * (i.e. setup_git_dir* has been run once)
-		 */
-		if (!saved_env_before_alias)
-			exit(run_builtin(builtin, argc, argv));
-	}
+	if (builtin)
+		exit(run_builtin(builtin, argc, argv));
 }
 
 static void execv_dashed_external(const char **argv)
@@ -586,8 +576,17 @@ static int run_argv(int *argcp, const char ***argv)
 	int done_alias = 0;
 
 	while (1) {
-		/* See if it's a builtin */
-		handle_builtin(*argcp, *argv);
+		/*
+		 * If we tried alias and futzed with our environment,
+		 * it no longer is safe to invoke builtins directly in
+		 * general.  We have to spawn them as dashed externals.
+		 *
+		 * NEEDSWORK: if we can figure out cases
+		 * where it is safe to do, we can avoid spawning a new
+		 * process.
+		 */
+		if (!done_alias)
+			handle_builtin(*argcp, *argv);
 
 		/* .. then try the external ones */
 		execv_dashed_external(*argv);
@@ -598,9 +597,9 @@ static int run_argv(int *argcp, const char ***argv)
 		 */
 		if (done_alias)
 			break;
+		done_alias = 1;
 		if (!handle_alias(argcp, argv))
 			break;
-		done_alias = 1;
 	}
 
 	return done_alias;

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

* Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it
  2016-01-26 20:11   ` Junio C Hamano
@ 2016-01-27  6:49     ` Junio C Hamano
  2016-01-27  6:50       ` [PATCH 2/3] git: protect against unbalanced calls to {save,restore}_env() Junio C Hamano
                         ` (2 more replies)
  0 siblings, 3 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-01-27  6:49 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, git

Junio C Hamano <gitster@pobox.com> writes:

> I spoke too soon, I am afraid.
> ...
> I wonder if this would be better done as a multi-part series that
> goes like this:
> ...

So here is the first of the three-patch series to replace it.

-- >8 --
From: Junio C Hamano <gitster@pobox.com>
Date: Tue, 26 Jan 2016 11:46:53 -0800
Subject: [PATCH 1/3] git: remove an early return from save_env_before_alias()

When help.autocorrect is in effect, an attempt to auto-execute an
uniquely corrected result of a misspelled alias will result in an
irrelevant error message.  The codepath that causes this issue calls
save_env_before_alias() and restore_env() as a pair in handle_alias(),
and that happens twice.  A global variable orig_cwd is allocated and
holds the return value of getcwd() in save_env_before_alias(), which
is used in restore_env() to go back to that directory and freed.

However, save_env_before_alias() is not prepared to be called twice.
It returns early when it knows it has already been called, leaving
orig_cwd undefined, which is then checked in the second call to
restore_env(), and by that time, the memory that used to hold the
contents of orig_cwd is either freed or reused to hold something else,
and this is fed to chdir(), causing it to fail.

Fix this by making sure save_env() does do the saving when called.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 git.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/git.c b/git.c
index 98d4412..a57a4cb 100644
--- a/git.c
+++ b/git.c
@@ -30,8 +30,6 @@ static int saved_env_before_alias;
 static void save_env_before_alias(void)
 {
 	int i;
-	if (saved_env_before_alias)
-		return;
 	saved_env_before_alias = 1;
 	orig_cwd = xgetcwd();
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
-- 
2.7.0-366-g065efda

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

* [PATCH 2/3] git: protect against unbalanced calls to {save,restore}_env()
  2016-01-27  6:49     ` Junio C Hamano
@ 2016-01-27  6:50       ` Junio C Hamano
  2016-01-27 23:19         ` [PATCH v2 " Junio C Hamano
  2016-01-27  6:52       ` [PATCH 3/3] git: simplify environment save/restore logic Junio C Hamano
  2016-01-27  9:14       ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Duy Nguyen
  2 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2016-01-27  6:50 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, git

We made sure that save_env_before_alias() does not skip saving the
environment when asked to (which led to double free of orig_cwd in
restore_env()) with the previous step.  Protect against future
breakage where somebody adds new callers of these functions in an
unbalanced fashion.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 git.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/git.c b/git.c
index a57a4cb..e39b972 100644
--- a/git.c
+++ b/git.c
@@ -26,11 +26,15 @@ static const char *env_names[] = {
 };
 static char *orig_env[4];
 static int saved_env_before_alias;
+static int save_restore_env_balance;
 
 static void save_env_before_alias(void)
 {
 	int i;
 	saved_env_before_alias = 1;
+
+	assert(save_restore_env_balance == 0);
+	save_restore_env_balance = 1;
 	orig_cwd = xgetcwd();
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 		orig_env[i] = getenv(env_names[i]);
@@ -42,6 +46,9 @@ static void save_env_before_alias(void)
 static void restore_env(int external_alias)
 {
 	int i;
+
+	assert(save_restore_env_balance == 1);
+	save_restore_env_balance = 0;
 	if (!external_alias && orig_cwd && chdir(orig_cwd))
 		die_errno("could not move to %s", orig_cwd);
 	free(orig_cwd);
-- 
2.7.0-368-g4610598

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

* [PATCH 3/3] git: simplify environment save/restore logic
  2016-01-27  6:49     ` Junio C Hamano
  2016-01-27  6:50       ` [PATCH 2/3] git: protect against unbalanced calls to {save,restore}_env() Junio C Hamano
@ 2016-01-27  6:52       ` Junio C Hamano
  2016-01-27 23:22         ` [PATCH v2 " Junio C Hamano
  2016-01-27  9:14       ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Duy Nguyen
  2 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2016-01-27  6:52 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, git

The only code that cares about the value of the global variable
saved_env_before_alias after the previous fix is handle_builtin()
that turns into a glorified no-op when the variable is true, so the
logic could safely be lifted to its caller, i.e. the caller can
refrain from calling it when the variable is set.

This variable tells us if save_env_before_alias() was called (with
or without matching restore_env()), but the sole caller of the
function, handle_alias(), always calls it the first as thing, so it
essentially keeps track of the fact that handle_alias() has ever
been called.

It turns out that handle_builtin() and handle_alias() are called
only from one function in a way that the value of the variable
matters, which is run_argv(), and it already keeps track of the fact
that it called handle_alias().

So we can simplify the whole thing by:

- Change handle_builtin() to always make a direct call to the
  builtin implementation it finds, and make sure the caller
  refrains from calling it if handle_alias() has ever been
  called;

- Remove saved_env_before_alias variable, and instead use the
  local "done_alias" variable maintained inside run_argv() to
  make the above decision.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * I do not mean to say that it is unnecessary to plug the leak,
   which should be the fourth patch in this three-patch series, but
   it should rather be obvious to implement, so I omitted from this
   series, which is primarily meant to be "how about doing it this
   way" illustration.

 git.c | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/git.c b/git.c
index e39b972..c8d7b56 100644
--- a/git.c
+++ b/git.c
@@ -25,13 +25,11 @@ static const char *env_names[] = {
 	GIT_PREFIX_ENVIRONMENT
 };
 static char *orig_env[4];
-static int saved_env_before_alias;
 static int save_restore_env_balance;
 
 static void save_env_before_alias(void)
 {
 	int i;
-	saved_env_before_alias = 1;
 
 	assert(save_restore_env_balance == 0);
 	save_restore_env_balance = 1;
@@ -533,16 +531,8 @@ static void handle_builtin(int argc, const char **argv)
 	}
 
 	builtin = get_builtin(cmd);
-	if (builtin) {
-		/*
-		 * XXX: if we can figure out cases where it is _safe_
-		 * to do, we can avoid spawning a new process when
-		 * saved_env_before_alias is true
-		 * (i.e. setup_git_dir* has been run once)
-		 */
-		if (!saved_env_before_alias)
-			exit(run_builtin(builtin, argc, argv));
-	}
+	if (builtin)
+		exit(run_builtin(builtin, argc, argv));
 }
 
 static void execv_dashed_external(const char **argv)
@@ -586,8 +576,17 @@ static int run_argv(int *argcp, const char ***argv)
 	int done_alias = 0;
 
 	while (1) {
-		/* See if it's a builtin */
-		handle_builtin(*argcp, *argv);
+		/*
+		 * If we tried alias and futzed with our environment,
+		 * it no longer is safe to invoke builtins directly in
+		 * general.  We have to spawn them as dashed externals.
+		 *
+		 * NEEDSWORK: if we can figure out cases
+		 * where it is safe to do, we can avoid spawning a new
+		 * process.
+		 */
+		if (!done_alias)
+			handle_builtin(*argcp, *argv);
 
 		/* .. then try the external ones */
 		execv_dashed_external(*argv);
@@ -598,9 +597,9 @@ static int run_argv(int *argcp, const char ***argv)
 		 */
 		if (done_alias)
 			break;
+		done_alias = 1;
 		if (!handle_alias(argcp, argv))
 			break;
-		done_alias = 1;
 	}
 
 	return done_alias;
-- 
2.7.0-368-g4610598

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

* Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it
  2016-01-27  6:49     ` Junio C Hamano
  2016-01-27  6:50       ` [PATCH 2/3] git: protect against unbalanced calls to {save,restore}_env() Junio C Hamano
  2016-01-27  6:52       ` [PATCH 3/3] git: simplify environment save/restore logic Junio C Hamano
@ 2016-01-27  9:14       ` Duy Nguyen
  2016-01-27 12:01         ` Junio C Hamano
  2 siblings, 1 reply; 22+ messages in thread
From: Duy Nguyen @ 2016-01-27  9:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git Mailing List, Michael J Gruber

On Wed, Jan 27, 2016 at 1:49 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> I spoke too soon, I am afraid.
>> ...
>> I wonder if this would be better done as a multi-part series that
>> goes like this:
>> ...
>
> So here is the first of the three-patch series to replace it.

This is much better (the whole series, not just the first patch). We
probably should add a test about help.autocorrect though, maybe in the
first patch, because it's not tested at all in the test suite.
-- 
Duy

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

* Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it
  2016-01-27  9:14       ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Duy Nguyen
@ 2016-01-27 12:01         ` Junio C Hamano
  2016-01-27 23:18           ` [PATCH v2 1/3] git: remove an early return from save_env_before_alias() Junio C Hamano
  0 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2016-01-27 12:01 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Michael J Gruber

Duy Nguyen <pclouds@gmail.com> writes:

> On Wed, Jan 27, 2016 at 1:49 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> Junio C Hamano <gitster@pobox.com> writes:
>>
>>> I spoke too soon, I am afraid.
>>> ...
>>> I wonder if this would be better done as a multi-part series that
>>> goes like this:
>>> ...
>>
>> So here is the first of the three-patch series to replace it.
>
> This is much better (the whole series, not just the first patch). We
> probably should add a test about help.autocorrect though, maybe in the
> first patch, because it's not tested at all in the test suite.

Patches welcome.  Thanks.

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

* [PATCH v2 1/3] git: remove an early return from save_env_before_alias()
  2016-01-27 12:01         ` Junio C Hamano
@ 2016-01-27 23:18           ` Junio C Hamano
  2016-02-02 23:47             ` [PATCH v3 1/4] " Junio C Hamano
                               ` (3 more replies)
  0 siblings, 4 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-01-27 23:18 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Michael J Gruber

Junio C Hamano <gitster@pobox.com> writes:

> Duy Nguyen <pclouds@gmail.com> writes:
>
>> On Wed, Jan 27, 2016 at 1:49 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>> Junio C Hamano <gitster@pobox.com> writes:
>>>
>>>> I spoke too soon, I am afraid.
>>>> ...
>>>> I wonder if this would be better done as a multi-part series that
>>>> goes like this:
>>>> ...
>>>
>>> So here is the first of the three-patch series to replace it.
>>
>> This is much better (the whole series, not just the first patch). We
>> probably should add a test about help.autocorrect though, maybe in the
>> first patch, because it's not tested at all in the test suite.
>
> Patches welcome.  Thanks.

Here is an updated 1/3; this feature does not fit very well to any
category (it is certainly not basic, or command that is primarily
about objects or worktree; I just picked "Git tools" t9xxx as that
is the closest thing to "direct end user support").

-- >8 --
When help.autocorrect is in effect, an attempt to auto-execute an
uniquely corrected result of a misspelt alias will result in an
irrelevant error message.  The codepath that causes this calls
save_env_before_alias() and restore_env() in handle_alias(), and
that happens twice.  A global variable orig_cwd is allocated to hold
the return value of getcwd() in save_env_before_alias(), which is
then used in restore_env() to go back to that directory and finally
free(3)'d there.

However, save_env_before_alias() is not prepared to be called twice.
It returns early when it knows it has already been called, leaving
orig_cwd undefined, which is then checked in the second call to
restore_env(), and by that time, the memory that used to hold the
contents of orig_cwd is either freed or reused to hold something
else, and this is fed to chdir(2), causing it to fail.  Even if it
did not fail (i.e. reading of the already free'd piece of memory
yielded a directory path that we can chdir(2) to), it then gets
free(3)'d.

Fix this by making sure save_env() does do the saving when called.

While at it, add a minimal test for help.autocorrect facility.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 git.c                       |  2 --
 t/t9003-help-autocorrect.sh | 52 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 2 deletions(-)
 create mode 100755 t/t9003-help-autocorrect.sh

diff --git a/git.c b/git.c
index 98d4412..a57a4cb 100644
--- a/git.c
+++ b/git.c
@@ -30,8 +30,6 @@ static int saved_env_before_alias;
 static void save_env_before_alias(void)
 {
 	int i;
-	if (saved_env_before_alias)
-		return;
 	saved_env_before_alias = 1;
 	orig_cwd = xgetcwd();
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh
new file mode 100755
index 0000000..dfe95c9
--- /dev/null
+++ b/t/t9003-help-autocorrect.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='help.autocorrect finding a match'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	# An alias
+	git config alias.lgf "log --format=%s --first-parent" &&
+
+	# A random user-defined command
+	write_script git-distimdistim <<-EOF &&
+		echo distimdistim was called
+	EOF
+
+	PATH="$PATH:." &&
+	export PATH &&
+
+	git commit --allow-empty -m "a single log entry" &&
+
+	# Sanity check
+	git lgf >actual &&
+	echo "a single log entry" >expect &&
+	test_cmp expect actual &&
+
+	git distimdistim >actual &&
+	echo "distimdistim was called" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'autocorrect showing candidates' '
+	git config help.autocorrect 0 &&
+
+	test_must_fail git lfg 2>actual &&
+	sed -e "1,/^Did you mean this/d" actual | grep lgf &&
+
+	test_must_fail git distimdist 2>actual &&
+	sed -e "1,/^Did you mean this/d" actual | grep distimdistim
+'
+
+test_expect_success 'autocorrect running commands' '
+	git config help.autocorrect -1 &&
+
+	git lfg >actual &&
+	echo "a single log entry" >expect &&
+	test_cmp expect actual &&
+
+	git distimdist >actual &&
+	echo "distimdistim was called" >expect &&
+	test_cmp expect actual
+'
+
+test_done
-- 
2.7.0-368-gb6e04f9

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

* [PATCH v2 2/3] git: protect against unbalanced calls to {save,restore}_env()
  2016-01-27  6:50       ` [PATCH 2/3] git: protect against unbalanced calls to {save,restore}_env() Junio C Hamano
@ 2016-01-27 23:19         ` Junio C Hamano
  0 siblings, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-01-27 23:19 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, git

We made sure that save_env_before_alias() does not skip saving the
environment when asked to (which led to use-after-free of orig_cwd
in restore_env() in the buggy version) with the previous step.

Protect against future breakage where somebody adds new callers of
these functions in an unbalanced fashion.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 git.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/git.c b/git.c
index a57a4cb..e39b972 100644
--- a/git.c
+++ b/git.c
@@ -26,11 +26,15 @@ static const char *env_names[] = {
 };
 static char *orig_env[4];
 static int saved_env_before_alias;
+static int save_restore_env_balance;
 
 static void save_env_before_alias(void)
 {
 	int i;
 	saved_env_before_alias = 1;
+
+	assert(save_restore_env_balance == 0);
+	save_restore_env_balance = 1;
 	orig_cwd = xgetcwd();
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 		orig_env[i] = getenv(env_names[i]);
@@ -42,6 +46,9 @@ static void save_env_before_alias(void)
 static void restore_env(int external_alias)
 {
 	int i;
+
+	assert(save_restore_env_balance == 1);
+	save_restore_env_balance = 0;
 	if (!external_alias && orig_cwd && chdir(orig_cwd))
 		die_errno("could not move to %s", orig_cwd);
 	free(orig_cwd);
-- 
2.7.0-368-gb6e04f9

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

* [PATCH v2 3/3] git: simplify environment save/restore logic
  2016-01-27  6:52       ` [PATCH 3/3] git: simplify environment save/restore logic Junio C Hamano
@ 2016-01-27 23:22         ` Junio C Hamano
  2016-01-27 23:47           ` Junio C Hamano
  0 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2016-01-27 23:22 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, git

The only code that cares about the value of the global variable
saved_env_before_alias after the previous fix is handle_builtin()
that turns into a glorified no-op when the variable is true, so the
logic could safely be lifted to its caller, i.e. the caller can
refrain from calling it when the variable is set.

This variable tells us if save_env_before_alias() was called (with
or without matching restore_env()), but the sole caller of the
function, handle_alias(), always calls it as the first thing, so we
can consider that the variable essentially keeps track of the fact
that handle_alias() has ever been called.

It turns out that handle_builtin() and handle_alias() are called
only from one function in a way that the value of the variable
matters, which is run_argv(), and it already keeps track of the
fact that it already called handle_alias().

So we can simplify the whole thing by:

- Change handle_builtin() to always make a direct call to the
  builtin implementation it finds, and make sure the caller
  refrains from calling it if handle_alias() has ever been
  called;

- Remove saved_env_before_alias variable, and instead use the
  local "done_alias" variable maintained inside run_argv() to
  make the same decision.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * Patches 2 and 3 are with updated log messages; their patch text
   did not change.

 git.c | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/git.c b/git.c
index e39b972..c8d7b56 100644
--- a/git.c
+++ b/git.c
@@ -25,13 +25,11 @@ static const char *env_names[] = {
 	GIT_PREFIX_ENVIRONMENT
 };
 static char *orig_env[4];
-static int saved_env_before_alias;
 static int save_restore_env_balance;
 
 static void save_env_before_alias(void)
 {
 	int i;
-	saved_env_before_alias = 1;
 
 	assert(save_restore_env_balance == 0);
 	save_restore_env_balance = 1;
@@ -533,16 +531,8 @@ static void handle_builtin(int argc, const char **argv)
 	}
 
 	builtin = get_builtin(cmd);
-	if (builtin) {
-		/*
-		 * XXX: if we can figure out cases where it is _safe_
-		 * to do, we can avoid spawning a new process when
-		 * saved_env_before_alias is true
-		 * (i.e. setup_git_dir* has been run once)
-		 */
-		if (!saved_env_before_alias)
-			exit(run_builtin(builtin, argc, argv));
-	}
+	if (builtin)
+		exit(run_builtin(builtin, argc, argv));
 }
 
 static void execv_dashed_external(const char **argv)
@@ -586,8 +576,17 @@ static int run_argv(int *argcp, const char ***argv)
 	int done_alias = 0;
 
 	while (1) {
-		/* See if it's a builtin */
-		handle_builtin(*argcp, *argv);
+		/*
+		 * If we tried alias and futzed with our environment,
+		 * it no longer is safe to invoke builtins directly in
+		 * general.  We have to spawn them as dashed externals.
+		 *
+		 * NEEDSWORK: if we can figure out cases
+		 * where it is safe to do, we can avoid spawning a new
+		 * process.
+		 */
+		if (!done_alias)
+			handle_builtin(*argcp, *argv);
 
 		/* .. then try the external ones */
 		execv_dashed_external(*argv);
@@ -598,9 +597,9 @@ static int run_argv(int *argcp, const char ***argv)
 		 */
 		if (done_alias)
 			break;
+		done_alias = 1;
 		if (!handle_alias(argcp, argv))
 			break;
-		done_alias = 1;
 	}
 
 	return done_alias;
-- 
2.7.0-368-gb6e04f9

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

* Re: [PATCH v2 3/3] git: simplify environment save/restore logic
  2016-01-27 23:22         ` [PATCH v2 " Junio C Hamano
@ 2016-01-27 23:47           ` Junio C Hamano
  0 siblings, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-01-27 23:47 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, git

Junio C Hamano <gitster@pobox.com> writes:

> @@ -598,9 +597,9 @@ static int run_argv(int *argcp, const char ***argv)
>  		 */
>  		if (done_alias)
>  			break;
> +		done_alias = 1;
>  		if (!handle_alias(argcp, argv))
>  			break;
> -		done_alias = 1;
>  	}
>  
>  	return done_alias;

This hunk shouldn't be there; it changes the return value from this
function and breaks the whole thing.

What I will push out as part of 'pu' will have this fixed.

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

* [PATCH v3 1/4] git: remove an early return from save_env_before_alias()
  2016-01-27 23:18           ` [PATCH v2 1/3] git: remove an early return from save_env_before_alias() Junio C Hamano
@ 2016-02-02 23:47             ` Junio C Hamano
  2016-02-02 23:47             ` [PATCH v3 2/4] git: protect against unbalanced calls to {save,restore}_env() Junio C Hamano
                               ` (2 subsequent siblings)
  3 siblings, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-02-02 23:47 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Duy Nguyen, Michael J Gruber

When help.autocorrect is in effect, an attempt to auto-execute an
uniquely corrected result of a misspelt alias will result in an
irrelevant error message.  The codepath that causes this calls
save_env_before_alias() and restore_env() in handle_alias(), and
that happens twice.  A global variable orig_cwd is allocated to hold
the return value of getcwd() in save_env_before_alias(), which is
then used in restore_env() to go back to that directory and finally
free(3)'d there.

However, save_env_before_alias() is not prepared to be called twice.
It returns early when it knows it has already been called, leaving
orig_cwd undefined, which is then checked in the second call to
restore_env(), and by that time, the memory that used to hold the
contents of orig_cwd is either freed or reused to hold something
else, and this is fed to chdir(2), causing it to fail.  Even if it
did not fail (i.e. reading of the already free'd piece of memory
yielded a directory path that we can chdir(2) to), it then gets
free(3)'d.

Fix this by making sure save_env() does do the saving when called.

While at it, add a minimal test for help.autocorrect facility.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * For another review round before deciding what to write in the
   upcoming "What's cooking" report.

 git.c                       |  2 --
 t/t9003-help-autocorrect.sh | 52 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 2 deletions(-)
 create mode 100755 t/t9003-help-autocorrect.sh

diff --git a/git.c b/git.c
index 98d4412..a57a4cb 100644
--- a/git.c
+++ b/git.c
@@ -30,8 +30,6 @@ static int saved_env_before_alias;
 static void save_env_before_alias(void)
 {
 	int i;
-	if (saved_env_before_alias)
-		return;
 	saved_env_before_alias = 1;
 	orig_cwd = xgetcwd();
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh
new file mode 100755
index 0000000..dfe95c9
--- /dev/null
+++ b/t/t9003-help-autocorrect.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='help.autocorrect finding a match'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	# An alias
+	git config alias.lgf "log --format=%s --first-parent" &&
+
+	# A random user-defined command
+	write_script git-distimdistim <<-EOF &&
+		echo distimdistim was called
+	EOF
+
+	PATH="$PATH:." &&
+	export PATH &&
+
+	git commit --allow-empty -m "a single log entry" &&
+
+	# Sanity check
+	git lgf >actual &&
+	echo "a single log entry" >expect &&
+	test_cmp expect actual &&
+
+	git distimdistim >actual &&
+	echo "distimdistim was called" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'autocorrect showing candidates' '
+	git config help.autocorrect 0 &&
+
+	test_must_fail git lfg 2>actual &&
+	sed -e "1,/^Did you mean this/d" actual | grep lgf &&
+
+	test_must_fail git distimdist 2>actual &&
+	sed -e "1,/^Did you mean this/d" actual | grep distimdistim
+'
+
+test_expect_success 'autocorrect running commands' '
+	git config help.autocorrect -1 &&
+
+	git lfg >actual &&
+	echo "a single log entry" >expect &&
+	test_cmp expect actual &&
+
+	git distimdist >actual &&
+	echo "distimdistim was called" >expect &&
+	test_cmp expect actual
+'
+
+test_done
-- 
2.7.0-391-gcd29568

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

* [PATCH v3 2/4] git: protect against unbalanced calls to {save,restore}_env()
  2016-01-27 23:18           ` [PATCH v2 1/3] git: remove an early return from save_env_before_alias() Junio C Hamano
  2016-02-02 23:47             ` [PATCH v3 1/4] " Junio C Hamano
@ 2016-02-02 23:47             ` Junio C Hamano
  2016-02-02 23:48             ` [PATCH v3 3/4] git: simplify environment save/restore logic Junio C Hamano
  2016-02-02 23:50             ` [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done Junio C Hamano
  3 siblings, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-02-02 23:47 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Michael J Gruber

We made sure that save_env_before_alias() does not skip saving the
environment when asked to (which led to use-after-free of orig_cwd
in restore_env() in the buggy version) with the previous step.

Protect against future breakage where somebody adds new callers of
these functions in an unbalanced fashion.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 git.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/git.c b/git.c
index a57a4cb..e39b972 100644
--- a/git.c
+++ b/git.c
@@ -26,11 +26,15 @@ static const char *env_names[] = {
 };
 static char *orig_env[4];
 static int saved_env_before_alias;
+static int save_restore_env_balance;
 
 static void save_env_before_alias(void)
 {
 	int i;
 	saved_env_before_alias = 1;
+
+	assert(save_restore_env_balance == 0);
+	save_restore_env_balance = 1;
 	orig_cwd = xgetcwd();
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 		orig_env[i] = getenv(env_names[i]);
@@ -42,6 +46,9 @@ static void save_env_before_alias(void)
 static void restore_env(int external_alias)
 {
 	int i;
+
+	assert(save_restore_env_balance == 1);
+	save_restore_env_balance = 0;
 	if (!external_alias && orig_cwd && chdir(orig_cwd))
 		die_errno("could not move to %s", orig_cwd);
 	free(orig_cwd);
-- 
2.7.0-391-gcd29568

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

* [PATCH v3 3/4] git: simplify environment save/restore logic
  2016-01-27 23:18           ` [PATCH v2 1/3] git: remove an early return from save_env_before_alias() Junio C Hamano
  2016-02-02 23:47             ` [PATCH v3 1/4] " Junio C Hamano
  2016-02-02 23:47             ` [PATCH v3 2/4] git: protect against unbalanced calls to {save,restore}_env() Junio C Hamano
@ 2016-02-02 23:48             ` Junio C Hamano
  2016-02-02 23:50             ` [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done Junio C Hamano
  3 siblings, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-02-02 23:48 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Michael J Gruber

The only code that cares about the value of the global variable
saved_env_before_alias after the previous fix is handle_builtin()
that turns into a glorified no-op when the variable is true, so the
logic could safely be lifted to its caller, i.e. the caller can
refrain from calling it when the variable is set.

This variable tells us if save_env_before_alias() was called (with
or without matching restore_env()), but the sole caller of the
function, handle_alias(), always calls it as the first thing, so we
can consider that the variable essentially keeps track of the fact
that handle_alias() has ever been called.

It turns out that handle_builtin() and handle_alias() are called
only from one function in a way that the value of the variable
matters, which is run_argv(), and it already keeps track of the
fact that it already called handle_alias().

So we can simplify the whole thing by:

- Change handle_builtin() to always make a direct call to the
  builtin implementation it finds, and make sure the caller
  refrains from calling it if handle_alias() has ever been
  called;

- Remove saved_env_before_alias variable, and instead use the
  local "done_alias" variable maintained inside run_argv() to
  make the same decision.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 git.c | 27 +++++++++++++--------------
 1 file changed, 13 insertions(+), 14 deletions(-)

diff --git a/git.c b/git.c
index e39b972..1cbe267 100644
--- a/git.c
+++ b/git.c
@@ -25,13 +25,11 @@ static const char *env_names[] = {
 	GIT_PREFIX_ENVIRONMENT
 };
 static char *orig_env[4];
-static int saved_env_before_alias;
 static int save_restore_env_balance;
 
 static void save_env_before_alias(void)
 {
 	int i;
-	saved_env_before_alias = 1;
 
 	assert(save_restore_env_balance == 0);
 	save_restore_env_balance = 1;
@@ -533,16 +531,8 @@ static void handle_builtin(int argc, const char **argv)
 	}
 
 	builtin = get_builtin(cmd);
-	if (builtin) {
-		/*
-		 * XXX: if we can figure out cases where it is _safe_
-		 * to do, we can avoid spawning a new process when
-		 * saved_env_before_alias is true
-		 * (i.e. setup_git_dir* has been run once)
-		 */
-		if (!saved_env_before_alias)
-			exit(run_builtin(builtin, argc, argv));
-	}
+	if (builtin)
+		exit(run_builtin(builtin, argc, argv));
 }
 
 static void execv_dashed_external(const char **argv)
@@ -586,8 +576,17 @@ static int run_argv(int *argcp, const char ***argv)
 	int done_alias = 0;
 
 	while (1) {
-		/* See if it's a builtin */
-		handle_builtin(*argcp, *argv);
+		/*
+		 * If we tried alias and futzed with our environment,
+		 * it no longer is safe to invoke builtins directly in
+		 * general.  We have to spawn them as dashed externals.
+		 *
+		 * NEEDSWORK: if we can figure out cases
+		 * where it is safe to do, we can avoid spawning a new
+		 * process.
+		 */
+		if (!done_alias)
+			handle_builtin(*argcp, *argv);
 
 		/* .. then try the external ones */
 		execv_dashed_external(*argv);
-- 
2.7.0-391-gcd29568

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

* [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done
  2016-01-27 23:18           ` [PATCH v2 1/3] git: remove an early return from save_env_before_alias() Junio C Hamano
                               ` (2 preceding siblings ...)
  2016-02-02 23:48             ` [PATCH v3 3/4] git: simplify environment save/restore logic Junio C Hamano
@ 2016-02-02 23:50             ` Junio C Hamano
  2016-02-03  1:47               ` Eric Sunshine
  3 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2016-02-02 23:50 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Git Mailing List, Michael J Gruber

Just like we free orig_cwd, which is the value of the original
working directory saved in save_env_before_alias(), once we are
done with it, the contents of orig_env[] array, saved in the
save_env_before_alias() function should be freed; otherwise,
the second and subsequent calls to save/restore pair will leak
the memory allocated in save_env_before_alias().

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * This is new.

 git.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/git.c b/git.c
index 1cbe267..93f569d 100644
--- a/git.c
+++ b/git.c
@@ -54,10 +54,12 @@ static void restore_env(int external_alias)
 		if (external_alias &&
 		    !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
 			continue;
-		if (orig_env[i])
+		if (orig_env[i]) {
 			setenv(env_names[i], orig_env[i], 1);
-		else
+			free(orig_env[i]);
+		} else {
 			unsetenv(env_names[i]);
+		}
 	}
 }
 
-- 
2.7.0-391-gcd29568

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

* Re: [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done
  2016-02-02 23:50             ` [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done Junio C Hamano
@ 2016-02-03  1:47               ` Eric Sunshine
  2016-02-03  2:19                 ` Junio C Hamano
  0 siblings, 1 reply; 22+ messages in thread
From: Eric Sunshine @ 2016-02-03  1:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Duy Nguyen, Git Mailing List, Michael J Gruber

On Tue, Feb 2, 2016 at 6:50 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Just like we free orig_cwd, which is the value of the original
> working directory saved in save_env_before_alias(), once we are
> done with it, the contents of orig_env[] array, saved in the
> save_env_before_alias() function should be freed; otherwise,
> the second and subsequent calls to save/restore pair will leak
> the memory allocated in save_env_before_alias().
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> diff --git a/git.c b/git.c
> @@ -54,10 +54,12 @@ static void restore_env(int external_alias)
>                 if (external_alias &&
>                     !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
>                         continue;
> -               if (orig_env[i])
> +               if (orig_env[i]) {
>                         setenv(env_names[i], orig_env[i], 1);
> -               else
> +                       free(orig_env[i]);

Now that this is "well-protected"[1] against incorrect nesting, you
don't worry about the dangling pointers in static orig_env[], right?
(The same for the dangling pointer in static 'orig_cwd' after being
freed a bit earlier in this function, correct?)

[1]: via assert()

> +               } else {
>                         unsetenv(env_names[i]);
> +               }
>         }
>  }

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

* Re: [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done
  2016-02-03  1:47               ` Eric Sunshine
@ 2016-02-03  2:19                 ` Junio C Hamano
  0 siblings, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2016-02-03  2:19 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Duy Nguyen, Git Mailing List, Michael J Gruber

Eric Sunshine <sunshine@sunshineco.com> writes:

>> diff --git a/git.c b/git.c
>> @@ -54,10 +54,12 @@ static void restore_env(int external_alias)
>>                 if (external_alias &&
>>                     !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
>>                         continue;
>> -               if (orig_env[i])
>> +               if (orig_env[i]) {
>>                         setenv(env_names[i], orig_env[i], 1);
>> -               else
>> +                       free(orig_env[i]);
>
> Now that this is "well-protected"[1] against incorrect nesting, you
> don't worry about the dangling pointers in static orig_env[], right?
> (The same for the dangling pointer in static 'orig_cwd' after being
> freed a bit earlier in this function, correct?)

Correct.

I do not think we follow a style that requires "after freeing memory
pointed at by a variable, the variable must be assigned NULL".
Would it be a good idea?  I do not see a point in it.

Thanks.

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

end of thread, other threads:[~2016-02-03  2:19 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-26  7:37 [BUG] typo DWIMery with alias broken (cd to random dir) Michael J Gruber
2016-01-26 12:50 ` Duy Nguyen
2016-01-26 13:26 ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Nguyễn Thái Ngọc Duy
2016-01-26 14:39   ` Michael J Gruber
2016-01-26 17:44     ` Junio C Hamano
2016-01-26 19:02   ` Junio C Hamano
2016-01-26 20:11   ` Junio C Hamano
2016-01-27  6:49     ` Junio C Hamano
2016-01-27  6:50       ` [PATCH 2/3] git: protect against unbalanced calls to {save,restore}_env() Junio C Hamano
2016-01-27 23:19         ` [PATCH v2 " Junio C Hamano
2016-01-27  6:52       ` [PATCH 3/3] git: simplify environment save/restore logic Junio C Hamano
2016-01-27 23:22         ` [PATCH v2 " Junio C Hamano
2016-01-27 23:47           ` Junio C Hamano
2016-01-27  9:14       ` [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it Duy Nguyen
2016-01-27 12:01         ` Junio C Hamano
2016-01-27 23:18           ` [PATCH v2 1/3] git: remove an early return from save_env_before_alias() Junio C Hamano
2016-02-02 23:47             ` [PATCH v3 1/4] " Junio C Hamano
2016-02-02 23:47             ` [PATCH v3 2/4] git: protect against unbalanced calls to {save,restore}_env() Junio C Hamano
2016-02-02 23:48             ` [PATCH v3 3/4] git: simplify environment save/restore logic Junio C Hamano
2016-02-02 23:50             ` [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done Junio C Hamano
2016-02-03  1:47               ` Eric Sunshine
2016-02-03  2:19                 ` Junio C Hamano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.