diff mbox series

[v3] setup: don't die if realpath(3) fails on getcwd(3)

Message ID 68c66aef7ca4dba53faec9e6d2f3b70fe58ac33e.1653403877.git.kevin@kevinlocke.name (mailing list archive)
State Superseded
Headers show
Series [v3] setup: don't die if realpath(3) fails on getcwd(3) | expand

Commit Message

Kevin Locke May 24, 2022, 2:51 p.m. UTC
Prior to Git 2.35.0, git could be run from an inaccessible working
directory so long as the git repository specified by options and/or
environment variables was accessible.  For example:

    git init repo
    mkdir -p a/b
    cd a/b
    chmod u-x ..
    git -C "${PWD%/a/b}/repo" status

If this example seems a bit contrived, consider running with the
repository owner as a substitute UID (e.g. with runuser(1) or sudo(8))
without ensuring the working directory is accessible by that user.

The code added by e6f8861bd4 ("setup: introduce
startup_info->original_cwd") to preserve the working directory attempts
to normalize the path using strbuf_realpath().  If that fails, as in the
case above, it is treated as a fatal error.

This commit treats strbuf_realpath() errors as non-fatal.  If an error
occurs, setup_original_cwd() will continue without applying removal
prevention for cwd, resulting in the pre-2.35.0 behavior.  The risk
should be minimal, since git will not operate on a repository with
inaccessible ancestors, this behavior is only known to occur when cwd is
a descendant of the repository, an ancestor of cwd is inaccessible, and
no ancestors of the repository are inaccessible.

Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
---

Notes:
Changes since v2:
 * Use trace2_data_string(), rather than trace_printf(), to report
   realpath failure.

Changes since v1:
 * Set startup_info->original_cwd = NULL when strbuf_realpath() fails,
   rather than setting it to the un-normalized path.
 * Add a trace message when realpath fails to aid debugging.
 * Remove potential realpath failure cause from comment before it.
 * Improve format for reference to e6f8861bd4 in commit message.
 * Clarify when the pre-2.35.0 behavior may occur as a result of this
   commit in the commit message.
 * Remove 'Fixes:' tag from commit message.

 setup.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

Comments

Elijah Newren May 24, 2022, 3:21 p.m. UTC | #1
On Tue, May 24, 2022 at 7:51 AM Kevin Locke <kevin@kevinlocke.name> wrote:
>
> Prior to Git 2.35.0, git could be run from an inaccessible working
> directory so long as the git repository specified by options and/or
> environment variables was accessible.  For example:
>
>     git init repo
>     mkdir -p a/b
>     cd a/b
>     chmod u-x ..
>     git -C "${PWD%/a/b}/repo" status
>
> If this example seems a bit contrived, consider running with the
> repository owner as a substitute UID (e.g. with runuser(1) or sudo(8))
> without ensuring the working directory is accessible by that user.
>
> The code added by e6f8861bd4 ("setup: introduce
> startup_info->original_cwd") to preserve the working directory attempts
> to normalize the path using strbuf_realpath().  If that fails, as in the
> case above, it is treated as a fatal error.
>
> This commit treats strbuf_realpath() errors as non-fatal.  If an error
> occurs, setup_original_cwd() will continue without applying removal
> prevention for cwd, resulting in the pre-2.35.0 behavior.  The risk
> should be minimal, since git will not operate on a repository with
> inaccessible ancestors, this behavior is only known to occur when cwd is
> a descendant of the repository, an ancestor of cwd is inaccessible, and
> no ancestors of the repository are inaccessible.
>
> Signed-off-by: Kevin Locke <kevin@kevinlocke.name>
> ---
>
> Notes:
> Changes since v2:
>  * Use trace2_data_string(), rather than trace_printf(), to report
>    realpath failure.
>
> Changes since v1:
>  * Set startup_info->original_cwd = NULL when strbuf_realpath() fails,
>    rather than setting it to the un-normalized path.
>  * Add a trace message when realpath fails to aid debugging.
>  * Remove potential realpath failure cause from comment before it.
>  * Improve format for reference to e6f8861bd4 in commit message.
>  * Clarify when the pre-2.35.0 behavior may occur as a result of this
>    commit in the commit message.
>  * Remove 'Fixes:' tag from commit message.
>
>  setup.c | 18 +++++++++++++-----
>  1 file changed, 13 insertions(+), 5 deletions(-)
>
> diff --git a/setup.c b/setup.c
> index a7b36f3ffbf..38bd55cbac1 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -458,11 +458,19 @@ static void setup_original_cwd(void)
>          *     not startup_info->original_cwd.
>          */
>
> -       /* Normalize the directory */
> -       strbuf_realpath(&tmp, tmp_original_cwd, 1);
> -       free((char*)tmp_original_cwd);
> -       tmp_original_cwd = NULL;
> -       startup_info->original_cwd = strbuf_detach(&tmp, NULL);
> +       /* Try to normalize the directory. */
> +       if (strbuf_realpath(&tmp, tmp_original_cwd, 0)) {
> +               free((char*)tmp_original_cwd);
> +               tmp_original_cwd = NULL;
> +               startup_info->original_cwd = strbuf_detach(&tmp, NULL);
> +       } else {
> +               trace2_data_string("setup", the_repository,
> +                                  "realpath-path", tmp_original_cwd);
> +               trace2_data_string("setup", the_repository,
> +                                  "realpath-failure", strerror(errno));
> +               tmp_original_cwd = NULL;
> +               goto no_prevention_needed;
> +       }
>
>         /*
>          * Get our worktree; we only protect the current working directory
> --
> 2.35.1

Looks good to me:

Reviewed-by: Elijah Newren <newren@gmail.com>
Derrick Stolee May 24, 2022, 5:41 p.m. UTC | #2
On 5/24/2022 10:51 AM, Kevin Locke wrote:> -	/* Normalize the directory */
> -	strbuf_realpath(&tmp, tmp_original_cwd, 1);
> -	free((char*)tmp_original_cwd);
> -	tmp_original_cwd = NULL;
> -	startup_info->original_cwd = strbuf_detach(&tmp, NULL);
> +	/* Try to normalize the directory. */
> +	if (strbuf_realpath(&tmp, tmp_original_cwd, 0)) {
> +		free((char*)tmp_original_cwd);
> +		tmp_original_cwd = NULL;
> +		startup_info->original_cwd = strbuf_detach(&tmp, NULL);
> +	} else {
> +		trace2_data_string("setup", the_repository,
> +				   "realpath-path", tmp_original_cwd);
> +		trace2_data_string("setup", the_repository,
> +				   "realpath-failure", strerror(errno));
> +		tmp_original_cwd = NULL;

I didn't catch this in v2, but should this line instead be

		startup_info->original_cwd = NULL;

? Especially based on this comment:

> Changes since v1:
>  * Set startup_info->original_cwd = NULL when strbuf_realpath() fails,
>    rather than setting it to the un-normalized path.

Thanks,
-Stolee
Kevin Locke May 24, 2022, 6 p.m. UTC | #3
On Tue, 2022-05-24 at 13:41 -0400, Derrick Stolee wrote:
> On 5/24/2022 10:51 AM, Kevin Locke wrote:> -	/* Normalize the directory */
>> -	strbuf_realpath(&tmp, tmp_original_cwd, 1);
>> -	free((char*)tmp_original_cwd);
>> -	tmp_original_cwd = NULL;
>> -	startup_info->original_cwd = strbuf_detach(&tmp, NULL);
>> +	/* Try to normalize the directory. */
>> +	if (strbuf_realpath(&tmp, tmp_original_cwd, 0)) {
>> +		free((char*)tmp_original_cwd);
>> +		tmp_original_cwd = NULL;
>> +		startup_info->original_cwd = strbuf_detach(&tmp, NULL);
>> +	} else {
>> +		trace2_data_string("setup", the_repository,
>> +				   "realpath-path", tmp_original_cwd);
>> +		trace2_data_string("setup", the_repository,
>> +				   "realpath-failure", strerror(errno));
>> +		tmp_original_cwd = NULL;
> 
> I didn't catch this in v2, but should this line instead be
> 
> 		startup_info->original_cwd = NULL;
> 
> ? Especially based on this comment:

No worries.  It's a good question.  Setting startup_info->original_cwd
to NULL is handled by the no_prevention_needed label.  But I just
realized it's not actually required in this case, since original_cwd
is NULL when setup_original_cwd() is called.  I should probably return
rather than jumping to no_prevention_needed from the else block to
avoid the unnecessary free(NULL) and assignment.

Your comment made me realize that v2 and later neglect to free
tmp_original_cwd when strbuf_realpath() fails.  How embarrassing.

I'll send an update to fix both those issues shortly.

Thanks again,
Kevin
diff mbox series

Patch

diff --git a/setup.c b/setup.c
index a7b36f3ffbf..38bd55cbac1 100644
--- a/setup.c
+++ b/setup.c
@@ -458,11 +458,19 @@  static void setup_original_cwd(void)
 	 *     not startup_info->original_cwd.
 	 */
 
-	/* Normalize the directory */
-	strbuf_realpath(&tmp, tmp_original_cwd, 1);
-	free((char*)tmp_original_cwd);
-	tmp_original_cwd = NULL;
-	startup_info->original_cwd = strbuf_detach(&tmp, NULL);
+	/* Try to normalize the directory. */
+	if (strbuf_realpath(&tmp, tmp_original_cwd, 0)) {
+		free((char*)tmp_original_cwd);
+		tmp_original_cwd = NULL;
+		startup_info->original_cwd = strbuf_detach(&tmp, NULL);
+	} else {
+		trace2_data_string("setup", the_repository,
+				   "realpath-path", tmp_original_cwd);
+		trace2_data_string("setup", the_repository,
+				   "realpath-failure", strerror(errno));
+		tmp_original_cwd = NULL;
+		goto no_prevention_needed;
+	}
 
 	/*
 	 * Get our worktree; we only protect the current working directory