threads / bug / 41259

[BUG] typo DWIMery with alias broken (cd to random dir)

Subject: [BUG] typo DWIMery with alias broken (cd to random dir)

## tl;dr

22 messages between Jan 26, 2016 and Feb 3, 2016.

replies: 21people: 4as markdown or json

Michael J Gruber· Jan 26, 2016, 07:37 UTC · lore
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
Duy Nguyen· Jan 26, 2016, 12:50 UTC · re: Michael J Gruber · lore

Re: [BUG] typo DWIMery with alias broken (cd to random dir)

On Tue, Jan 26, 2016 at 2:37 PM, Michael J Gruber <git@drmicha.warpmail.net> wrote:

Show 20 quoted lines
> 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
Nguyễn Thái Ngọc Duy· Jan 26, 2016, 13:26 UTC · re: Michael J Gruber · lore

[PATCH] git.c: fix help.autocorrect after 57ea712 breaks it

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
Michael J Gruber· Jan 26, 2016, 14:39 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it

Nguyễn Thái Ngọc Duy venit, vidit, dixit 26.01.2016 14:26:
Show 22 quoted lines
> 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
Junio C Hamano· Jan 26, 2016, 17:44 UTC · re: Michael J Gruber · lore

Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it

Michael J Gruber <git@drmicha.warpmail.net> writes:
Show 27 quoted lines
> 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.

Junio C Hamano· Jan 26, 2016, 19:02 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
Show 10 quoted lines
> 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?

Show 43 quoted lines
>  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
Junio C Hamano· Jan 26, 2016, 20:11 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
Show 22 quoted lines
> 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;
Junio C Hamano· Jan 27, 2016, 06:49 UTC · re: Junio C Hamano · lore

Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it

Junio C Hamano <gitster@pobox.com> writes:
Show 5 quoted lines
> 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
Junio C Hamano· Jan 27, 2016, 06:50 UTC · re: Junio C Hamano · lore

[PATCH 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.

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
Junio C Hamano· Jan 27, 2016, 23:19 UTC · re: Junio C Hamano · lore

[PATCH v2 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 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
Junio C Hamano· Jan 27, 2016, 06:52 UTC · re: Junio C Hamano · lore

[PATCH 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.
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
Junio C Hamano· Jan 27, 2016, 23:22 UTC · re: Junio C Hamano · lore

[PATCH v2 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 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
Junio C Hamano· Jan 27, 2016, 23:47 UTC · re: Junio C Hamano · lore

Re: [PATCH v2 3/3] git: simplify environment save/restore logic

Junio C Hamano <gitster@pobox.com> writes:
Show 11 quoted lines
> @@ -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.
Duy Nguyen· Jan 27, 2016, 09:14 UTC · re: Junio C Hamano · lore

Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it

On Wed, Jan 27, 2016 at 1:49 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 9 quoted lines
> 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
Junio C Hamano· Jan 27, 2016, 12:01 UTC · re: Duy Nguyen · lore

Re: [PATCH] git.c: fix help.autocorrect after 57ea712 breaks it

Duy Nguyen <pclouds@gmail.com> writes:
Show 14 quoted lines
> 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.
Junio C Hamano· Jan 27, 2016, 23:18 UTC · re: Junio C Hamano · lore

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

Junio C Hamano <gitster@pobox.com> writes:
Show 18 quoted lines
> 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
Junio C Hamano· Feb 2, 2016, 23:47 UTC · re: Junio C Hamano · lore

[PATCH v3 1/4] 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 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
Junio C Hamano· Feb 2, 2016, 23:47 UTC · re: Junio C Hamano · lore

[PATCH v3 2/4] 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 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
Junio C Hamano· Feb 2, 2016, 23:48 UTC · re: Junio C Hamano · lore

[PATCH v3 3/4] 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 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
Junio C Hamano· Feb 2, 2016, 23:50 UTC · re: Junio C Hamano · lore

[PATCH v3 4/4] restore_env(): free the saved environment variable once we are done

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
Eric Sunshine· Feb 3, 2016, 01:47 UTC · re: Junio C Hamano · lore

Re: [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done

On Tue, Feb 2, 2016 at 6:50 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 19 quoted lines
> 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()
Show 5 quoted lines
> +               } else {
>                         unsetenv(env_names[i]);
> +               }
>         }
>  }
Junio C Hamano· Feb 3, 2016, 02:19 UTC · re: Eric Sunshine · lore

Re: [PATCH v3 4/4] restore_env(): free the saved environment variable once we are done

Eric Sunshine <sunshine@sunshineco.com> writes:
Show 15 quoted lines
>> 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.

← back to recent threads