[RFC PATCH] rev-parse: fix segfault with missing --path-format argument

Subsystems: the rest

STALE1901d

8 messages, 2 authors, 2021-05-19 · open the first message on its own page

[RFC PATCH] rev-parse: fix segfault with missing --path-format argument

From: Wolfgang Müller <hidden>
Date: 2021-05-16 12:14:15

Calling "git rev-parse --path-format" without an argument segfaults
instead of giving an error message. Commit fac60b8925 (rev-parse: add
option for absolute or relative path formatting, 2020-12-13) added the
argument parsing code but forgot to handle NULL.

Returning an error makes sense here because there is no default value we
could use. Add a test case to verify.

Signed-off-by: Wolfgang Müller <redacted>
---
Since this is my first contribution to the project and I'm still
learning the ropes, I left this patch as RFC.

For a bit of background information, I ran into this expecting the
following to work:

	git rev-parse --path-format relative --show-toplevel

I'm unsure how many git subcommands specifically require "=" between the
option and the argument, but before now I always expected things to
"just work" when leaving it out.

This fix is based on maint.

Thanks for your time and attention.

 builtin/rev-parse.c  | 2 ++
 t/t1500-rev-parse.sh | 4 ++++
 2 files changed, 6 insertions(+)
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 85bad9052e..7af8dab8bc 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -759,6 +759,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				continue;
 			}
 			if (opt_with_value(arg, "--path-format", &arg)) {
+				if (!arg)
+					die("--path-format requires an argument");
 				if (!strcmp(arg, "absolute")) {
 					format = FORMAT_CANONICAL;
 				} else if (!strcmp(arg, "relative")) {
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index deae916707..a1a8ce5265 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -146,6 +146,10 @@ test_expect_success '--path-format can change in the middle of the command line'
 	test_cmp expect actual
 '
 
+test_expect_success '--path-format does not segfault without an argument' '
+	test_must_fail git rev-parse --path-format --show-toplevel
+'
+
 test_expect_success 'git-common-dir from worktree root' '
 	echo .git >expect &&
 	git rev-parse --git-common-dir >actual &&
-- 
2.31.1

[PATCH v2 0/2] rev-parse: Fix segfault and translate messages

From: Wolfgang Müller <hidden>
Date: 2021-05-17 08:03:21

This series fixes a segfault in rev-parse and, whilst we're there, marks
previously untranslated error messages for translation.

Compared to v1, this series simplifies the test for --path-format
without and argument and adds the translation fix on top.

Wolfgang Müller (2):
  rev-parse: fix segfault with missing --path-format argument
  rev-parse: Mark die() messages for translation

 builtin/rev-parse.c  | 30 ++++++++++++++++--------------
 t/t1500-rev-parse.sh |  4 ++++
 2 files changed, 20 insertions(+), 14 deletions(-)

-- 
2.31.1

[PATCH v2 1/2] rev-parse: fix segfault with missing --path-format argument

From: Wolfgang Müller <hidden>
Date: 2021-05-17 08:03:22

Calling "git rev-parse --path-format" without an argument segfaults
instead of giving an error message. Commit fac60b8925 (rev-parse: add
option for absolute or relative path formatting, 2020-12-13) added the
argument parsing code but forgot to handle NULL.

Returning an error makes sense here because there is no default value we
could use. Add a test case to verify.

Signed-off-by: Wolfgang Müller <redacted>
---
 builtin/rev-parse.c  | 2 ++
 t/t1500-rev-parse.sh | 4 ++++
 2 files changed, 6 insertions(+)
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 85bad9052e..7af8dab8bc 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -759,6 +759,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				continue;
 			}
 			if (opt_with_value(arg, "--path-format", &arg)) {
+				if (!arg)
+					die("--path-format requires an argument");
 				if (!strcmp(arg, "absolute")) {
 					format = FORMAT_CANONICAL;
 				} else if (!strcmp(arg, "relative")) {
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index deae916707..1c2df08333 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -146,6 +146,10 @@ test_expect_success '--path-format can change in the middle of the command line'
 	test_cmp expect actual
 '
 
+test_expect_success '--path-format does not segfault without an argument' '
+	test_must_fail git rev-parse --path-format
+'
+
 test_expect_success 'git-common-dir from worktree root' '
 	echo .git >expect &&
 	git rev-parse --git-common-dir >actual &&
-- 
2.31.1

Re: [PATCH v2 1/2] rev-parse: fix segfault with missing --path-format argument

From: Jeff King <hidden>
Date: 2021-05-17 08:18:48

On Mon, May 17, 2021 at 10:02:42AM +0200, Wolfgang Müller wrote:
quoted hunk
Calling "git rev-parse --path-format" without an argument segfaults
instead of giving an error message. Commit fac60b8925 (rev-parse: add
option for absolute or relative path formatting, 2020-12-13) added the
argument parsing code but forgot to handle NULL.

Returning an error makes sense here because there is no default value we
could use. Add a test case to verify.

Signed-off-by: Wolfgang Müller <redacted>
---
 builtin/rev-parse.c  | 2 ++
 t/t1500-rev-parse.sh | 4 ++++
 2 files changed, 6 insertions(+)
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 85bad9052e..7af8dab8bc 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -759,6 +759,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				continue;
 			}
 			if (opt_with_value(arg, "--path-format", &arg)) {
+				if (!arg)
+					die("--path-format requires an argument");
 				if (!strcmp(arg, "absolute")) {
 					format = FORMAT_CANONICAL;
 				} else if (!strcmp(arg, "relative")) {
This looks like a fine solution, but I do think this code using
opt_with_value() is a bit of a curiosity in the first place. I looked at
the other callers (because I wanted to see if there were similar
problems), and they are all cases where the argument is truly optional
(so matching "--foo" or "--foo=bar" is correct, and matching "--foo bar"
would be actively wrong).

For cases where the argument is not optional, we seem to use
skip_prefix(), like:
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index c4263404bd..9553cc7c10 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -760,7 +760,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 					show(arg);
 				continue;
 			}
-			if (opt_with_value(arg, "--path-format", &arg)) {
+			if (skip_prefix(arg, "--path-format=", &arg)) {
 				if (!strcmp(arg, "absolute")) {
 					format = FORMAT_CANONICAL;
 				} else if (!strcmp(arg, "relative")) {
I don't have a strong preference for one or the other. It is maybe
helpful to diagnose "--path-format" without an equals as an error, as
your patch would, rather than quietly passing it along as an unknown (as
the hunk above would). I dunno. That perhaps applies to the rest of the
options, too. :)

(In an ideal world, we would probably match "--path-format <arg>" here,
as our usual parse-options API does. But that is a much bigger change).

-Peff

Re: [PATCH v2 1/2] rev-parse: fix segfault with missing --path-format argument

From: Wolfgang Müller <hidden>
Date: 2021-05-19 09:53:39

On 2021-05-17 04:16, Jeff King wrote:
I don't have a strong preference for one or the other. It is maybe
helpful to diagnose "--path-format" without an equals as an error, as
your patch would, rather than quietly passing it along as an unknown
(as the hunk above would). I dunno. That perhaps applies to the rest
of the options, too. :)
I have a very slight preference for throwing an error: that is what I
expected to happen as a user. At the same time, passing it along as an
unknown seems more consistent with the other options that take equals.
I'm split on this, and would defer to what people here prefer.

Short of fully migrating to the parse-options API, I do not see a
perfect solution for this, especially since there are options that take
optional arguments which are not delimited by equals. These seem to be
sprinkled throughout and all error out if no argument is given.

Here's a small selection:

	Option                   Section in manual        Parser

	--default <arg>          Options for Output       manual
	--prefix <arg>           Options for Output       manual
	--short[=length]         Options for Output       opt_with_value
	--path-format=[..]       Options for Files        opt_with_value
	--git-path <path>        Options for Files        manual
	--disambiguate=<prefix>  Options for Objects      skip_prefix

Out of curiosity I decided to dig around a bit, my hunch being that
arguments without equals are older.

I can trace --default back to 178cb24338 (Add 'git-rev-parse' helper
script, 2005-06-13). That seems to be the very first version of
git-rev-parse.

The first options with equals, --since= et al., were added in c1babb1d65
([PATCH] Teach "git-rev-parse" about date-based cut-offs, 2005-09-20)

The --short option used to be --abbrev, and was added in d50125085a
(rev-parse: --abbrev option., 2006-01-25). Then, quite a bit later, the
second option without equals was added in abc06822af (rev-parse: add
option --resolve-git-dir <path>, 2011-08-15).

--prefix goes back to 12b9d32790 (rev-parse: add --prefix option,
2013-06-16) and --git-path is 557bd833bb (git_path(): be aware of file
relocation in $GIT_DIR, 2014-11-30)

So it turns out that my hunch was not really correct. Maybe there's also
a pattern that I am not seeing. Obviously this has no bearing on the
segfault fix, but is maybe valuable information for a conversion to the
parse-options API down the line. It was also fun to figure out, since I
could not stop thinking about rev-parse having a bunch of different
option semantics.

-- 
Wolfgang

Re: [PATCH v2 1/2] rev-parse: fix segfault with missing --path-format argument

From: Wolfgang Müller <hidden>
Date: 2021-05-19 10:19:36

On 2021-05-19 11:52, Wolfgang Müller wrote:
Short of fully migrating to the parse-options API, I do not see a
perfect solution for this, especially since there are options that take
optional arguments which are not delimited by equals. These seem to be
sprinkled throughout and all error out if no argument is given.
Upon rereading this I noticed an error, I meant to say "especially since
there are options that *require* arguments which are not delimited by
equals.", like --default, --prefix, and --git-path for example.

-- 
Wolfgang

Re: [PATCH v2 1/2] rev-parse: fix segfault with missing --path-format argument

From: Jeff King <hidden>
Date: 2021-05-19 14:21:28

On Wed, May 19, 2021 at 11:52:35AM +0200, Wolfgang Müller wrote:
On 2021-05-17 04:16, Jeff King wrote:
quoted
I don't have a strong preference for one or the other. It is maybe
helpful to diagnose "--path-format" without an equals as an error, as
your patch would, rather than quietly passing it along as an unknown
(as the hunk above would). I dunno. That perhaps applies to the rest
of the options, too. :)
I have a very slight preference for throwing an error: that is what I
expected to happen as a user. At the same time, passing it along as an
unknown seems more consistent with the other options that take equals.
I'm split on this, and would defer to what people here prefer.
Yeah, I don't feel strongly at all. I like consistency, but noticing
bogus input is good, too. ;)
Short of fully migrating to the parse-options API, I do not see a
perfect solution for this, especially since there are options that take
optional arguments which are not delimited by equals. These seem to be
sprinkled throughout and all error out if no argument is given.
Ultimately I think using the parse-options API would be nice. In the
meantime, I suspect (but didn't think too hard on it) that you could get
by with two helpers:

  - rename the current opt_with_value() to opt_with_optional_value()
    to make its assumptions clear.

  - add a new helper opt_with_required_value() that accepts either:

      --default <arg>
      --default=<arg>
      --disambiguate <arg>
      --disambiguate=<arg>
      etc...

     and complains when the "=" is missing, or there is no extra "<arg>"
     available.

The second helper is a little tricky to write, because it sometimes
needs to advance argv (and decrement argc) to account for the extra
consumed arg.

That's definitely something we can leave off for now, though.
So it turns out that my hunch was not really correct. Maybe there's also
a pattern that I am not seeing.
I don't find it hard to believe that there's no obvious pattern. :) I
would say that rev-parse is one of the messiest and most "organically
grown" parts of Git.

Thanks for digging, though. It is always nice to see contributors going
the extra mile to figure out how their solutions fit into the bigger
picture of the project.

-Peff

[PATCH v2 2/2] rev-parse: Mark die() messages for translation

From: Wolfgang Müller <hidden>
Date: 2021-05-17 08:03:23

These error messages are intended for the user. Let's touch them up
since we're here from the previous commit.

Signed-off-by: Wolfgang Müller <redacted>
---
 builtin/rev-parse.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 7af8dab8bc..22c4e1a4ff 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -435,11 +435,11 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
 	/* get the usage up to the first line with a -- on it */
 	for (;;) {
 		if (strbuf_getline(&sb, stdin) == EOF)
-			die("premature end of input");
+			die(_("premature end of input"));
 		ALLOC_GROW(usage, unb + 1, usz);
 		if (!strcmp("--", sb.buf)) {
 			if (unb < 1)
-				die("no usage string given before the `--' separator");
+				die(_("no usage string given before the `--' separator"));
 			usage[unb] = NULL;
 			break;
 		}
@@ -545,7 +545,7 @@ static void die_no_single_rev(int quiet)
 	if (quiet)
 		exit(1);
 	else
-		die("Needed a single revision");
+		die(_("Needed a single revision"));
 }
 
 static const char builtin_rev_parse_usage[] =
@@ -709,10 +709,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 			if (!strcmp(arg, "--resolve-git-dir")) {
 				const char *gitdir = argv[++i];
 				if (!gitdir)
-					die("--resolve-git-dir requires an argument");
+					die(_("--resolve-git-dir requires an argument"));
 				gitdir = resolve_gitdir(gitdir);
 				if (!gitdir)
-					die("not a gitdir '%s'", argv[i]);
+					die(_("not a gitdir '%s'"), argv[i]);
 				puts(gitdir);
 				continue;
 			}
@@ -736,7 +736,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 		if (!seen_end_of_options && *arg == '-') {
 			if (!strcmp(arg, "--git-path")) {
 				if (!argv[i + 1])
-					die("--git-path requires an argument");
+					die(_("--git-path requires an argument"));
 				strbuf_reset(&buf);
 				print_path(git_path("%s", argv[i + 1]), prefix,
 						format,
@@ -746,7 +746,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 			}
 			if (!strcmp(arg,"-n")) {
 				if (++i >= argc)
-					die("-n requires an argument");
+					die(_("-n requires an argument"));
 				if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
 					show(arg);
 					show(argv[i]);
@@ -760,26 +760,26 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 			}
 			if (opt_with_value(arg, "--path-format", &arg)) {
 				if (!arg)
-					die("--path-format requires an argument");
+					die(_("--path-format requires an argument"));
 				if (!strcmp(arg, "absolute")) {
 					format = FORMAT_CANONICAL;
 				} else if (!strcmp(arg, "relative")) {
 					format = FORMAT_RELATIVE;
 				} else {
-					die("unknown argument to --path-format: %s", arg);
+					die(_("unknown argument to --path-format: %s"), arg);
 				}
 				continue;
 			}
 			if (!strcmp(arg, "--default")) {
 				def = argv[++i];
 				if (!def)
-					die("--default requires an argument");
+					die(_("--default requires an argument"));
 				continue;
 			}
 			if (!strcmp(arg, "--prefix")) {
 				prefix = argv[++i];
 				if (!prefix)
-					die("--prefix requires an argument");
+					die(_("--prefix requires an argument"));
 				startup_info->prefix = prefix;
 				output_prefix = 1;
 				continue;
@@ -848,7 +848,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 					else if (!strcmp(arg, "loose"))
 						abbrev_ref_strict = 0;
 					else
-						die("unknown mode for --abbrev-ref: %s",
+						die(_("unknown mode for --abbrev-ref: %s"),
 						    arg);
 				}
 				continue;
@@ -892,7 +892,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				if (work_tree)
 					print_path(work_tree, prefix, format, DEFAULT_UNMODIFIED);
 				else
-					die("this operation must be run in a work tree");
+					die(_("this operation must be run in a work tree"));
 				continue;
 			}
 			if (!strcmp(arg, "--show-superproject-working-tree")) {
@@ -1020,7 +1020,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				if (strcmp(val, "storage") &&
 				    strcmp(val, "input") &&
 				    strcmp(val, "output"))
-					die("unknown mode for --show-object-format: %s",
+					die(_("unknown mode for --show-object-format: %s"),
 					    arg);
 				puts(the_hash_algo->name);
 				continue;
@@ -1058,7 +1058,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 		if (verify)
 			die_no_single_rev(quiet);
 		if (has_dashdash)
-			die("bad revision '%s'", arg);
+			die(_("bad revision '%s'"), arg);
 		as_is = 1;
 		if (!show_file(arg, output_prefix))
 			continue;
-- 
2.31.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help