From: Matthew Hughes <hidden> Date: 2021-06-28 12:17:54
Hi all,
It's my first time writing to this mailing list, so apologies in advance
for any mistakes/faux pas.
Summary
=======
Segfault seen when running:
$ ./git grep --and -e 'pattern' -- not_a_path
Expected Behaviour
==================
The above invocation doesn't appear particularly valid to me, I
accidentally ran it while editing a command. Not sure if this needs
addressing, but perhaps the expected behaviour would be an error message
about it not being valid?
Compare with, running without '--':
$ ./git grep --and -e 'pattern' not_a_path
fatal: ambiguous argument 'not_a_path': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
Running with a valid path:
$ ./git grep --and -e 'pattern' -- common-main.c
fatal: Not a valid grep expression
$ ./git grep --and -e 'pattern' common-main.c
fatal: Not a valid grep expression
Background
==========
git version:
git version 2.32.0.93.g670b81a890
cpu: x86_64
built from commit: 670b81a890388c60b7032a4f5b879f2ece8c4558
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
uname: Linux 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64
compiler info: gnuc: 8.3
libc info: glibc: 2.28
$SHELL (typically, interactive shell): /bin/bash
Also reproduced on git 2.20.1 (packaged version on Debian 10)
The segfault is raised at free_pattern_expr (x=0x0) at grep.c:825 (on
commit 670b81a890388c60b7032a4f5b879f2ece8c4558)
Regards,
Matt
From: René Scharfe <hidden> Date: 2021-06-28 18:58:23
Git grep allows combining two patterns with --and. It checks and
reports if the second pattern is missing when compiling the expression.
A missing first pattern, however, is only reported later at match time.
Thus no error is returned if no matching is done, e.g. because no file
matches the also given pathspec.
When that happens we get an expression tree with an GREP_NODE_AND node
and a NULL pointer to the missing left child. free_pattern_expr()
tries to dereference it during the cleanup at the end, which result in
a segmentation fault.
Fix this by verifying the presence of the left operand at expression
compilation time.
Reported-by: Matthew Hughes <redacted>
Signed-off-by: René Scharfe <redacted>
---
Whether the check in match_expr_eval() can now be turned into a BUG is
left as an exercise for the reader. ;-)
grep.c | 2 ++
t/t7810-grep.sh | 10 ++++++++++
2 files changed, 12 insertions(+)
@@ -89,6 +96,9 @@ test_expect_success 'grep should not segfault with a bad input' 'test_must_failgitgrep"("'+test_invalid_grep_expression-eA--and+test_invalid_grep_expression--and-eA+forHinHEAD''docase"$H"in--
Hi all,
It's my first time writing to this mailing list, so apologies in advance
for any mistakes/faux pas.
Thanks a lot for the report, and welcome.
Summary
=======
Segfault seen when running:
$ ./git grep --and -e 'pattern' -- not_a_path
Expected Behaviour
==================
The above invocation doesn't appear particularly valid to me, I
accidentally ran it while editing a command. Not sure if this needs
addressing, but perhaps the expected behaviour would be an error message
about it not being valid?
Compare with, running without '--':
$ ./git grep --and -e 'pattern' not_a_path
fatal: ambiguous argument 'not_a_path': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
Running with a valid path:
$ ./git grep --and -e 'pattern' -- common-main.c
fatal: Not a valid grep expression
$ ./git grep --and -e 'pattern' common-main.c
fatal: Not a valid grep expression
The exact segfault you've found is found at least back to Git version
2.0.0, so it's been with us for a while.
The problem is that you're trying to --and two things, but did not
provide two things. We apparently never checked for this, and then
segfault in the cleanup, thinking an --and must imply that we have two
sides.
I haven't looked deeply, but think that the reason it segfaults just
with not_a_path is that we then proceed to die() in something that
should probably be a BUG() instead as we can't grep in the
expected-but-not-there pattern at runtime.
Giving it a pathspec that doesn't exist means we never run the "real"
grep, but we do run the cleanup, hence the segfault.
Git grep allows combining two patterns with --and. It checks and
reports if the second pattern is missing when compiling the expression.
A missing first pattern, however, is only reported later at match time.
Thus no error is returned if no matching is done, e.g. because no file
matches the also given pathspec.
When that happens we get an expression tree with an GREP_NODE_AND node
and a NULL pointer to the missing left child. free_pattern_expr()
tries to dereference it during the cleanup at the end, which result in
a segmentation fault.
Fix this by verifying the presence of the left operand at expression
compilation time.
Reported-by: Matthew Hughes <redacted>
Signed-off-by: René Scharfe <redacted>
---
Whether the check in match_expr_eval() can now be turned into a BUG is
left as an exercise for the reader. ;-)
grep.c | 2 ++
t/t7810-grep.sh | 10 ++++++++++
2 files changed, 12 insertions(+)
@@ -89,6 +96,9 @@ test_expect_success 'grep should not segfault with a bad input' 'test_must_failgitgrep"("'+test_invalid_grep_expression-eA--and+test_invalid_grep_expression--and-eA+forHinHEAD''docase"$H"in
This seems like an incomplete fix, for the exact same thing with --or we
silently return 1, as we would if we exited early in free_pattern_expr
on !x, which aside from the segfault I think we should probably make a
habit in our own free()-like functions.
Whatever we're doing about the --and segfault it seems like we should do
the same under --or, no?
Your first test also passes before your fix, it's only the latter that
segfaults. The first one emits:
fatal: --and not followed by pattern expression
So having that in a leading patch to indicate no behavior was changed
would be better.
Instead of the "Not a valid grep expression" error let's instead say
something like:
fatal: --[and|or] must follow a pattern expression
The error (which I know you just copied from elsewhere) is misleading,
it's not the pattern that's not valid (as to me it implies), but our own
--and/--or option usage.
And the "excercise for the reader" is a bit flippant, do we actually hit
that condition now? If not and we're sure we won't now seems like the
time to add a BUG() there, and to change the "Not a valid grep
expression" to "internal error in --and/--or parsing" or something.
Thanks for the patch!
From: René Scharfe <hidden> Date: 2021-06-29 18:35:58
Am 29.06.21 um 19:52 schrieb Ævar Arnfjörð Bjarmason:
On Mon, Jun 28 2021, René Scharfe wrote:
quoted
Git grep allows combining two patterns with --and. It checks and
reports if the second pattern is missing when compiling the expression.
A missing first pattern, however, is only reported later at match time.
Thus no error is returned if no matching is done, e.g. because no file
matches the also given pathspec.
When that happens we get an expression tree with an GREP_NODE_AND node
and a NULL pointer to the missing left child. free_pattern_expr()
tries to dereference it during the cleanup at the end, which result in
a segmentation fault.
Fix this by verifying the presence of the left operand at expression
compilation time.
Reported-by: Matthew Hughes <redacted>
Signed-off-by: René Scharfe <redacted>
---
Whether the check in match_expr_eval() can now be turned into a BUG is
left as an exercise for the reader. ;-)
grep.c | 2 ++
t/t7810-grep.sh | 10 ++++++++++
2 files changed, 12 insertions(+)
@@ -89,6 +96,9 @@ test_expect_success 'grep should not segfault with a bad input' 'test_must_failgitgrep"("'+test_invalid_grep_expression-eA--and+test_invalid_grep_expression--and-eA+forHinHEAD''docase"$H"in
This seems like an incomplete fix, for the exact same thing with --or we
silently return 1, as we would if we exited early in free_pattern_expr
on !x, which aside from the segfault I think we should probably make a
habit in our own free()-like functions.
Whatever we're doing about the --and segfault it seems like we should do
the same under --or, no?
No, --or is a special case and needs special handling. Currently it's
ignored. If we want to berate the user for using it without expressions
left and right then we need to start actively handling it.
Your first test also passes before your fix, it's only the latter that
segfaults. The first one emits:
fatal: --and not followed by pattern expression
So having that in a leading patch to indicate no behavior was changed
would be better.
True, the first test is just nice to have. I can remove it to reduce
confusion.
Instead of the "Not a valid grep expression" error let's instead say
something like:
fatal: --[and|or] must follow a pattern expression
Good point.
The error (which I know you just copied from elsewhere) is misleading,
it's not the pattern that's not valid (as to me it implies), but our own
--and/--or option usage.
That's what's meant with extended pattern, I think.
And the "excercise for the reader" is a bit flippant, do we actually hit
that condition now? If not and we're sure we won't now seems like the
time to add a BUG() there, and to change the "Not a valid grep
expression" to "internal error in --and/--or parsing" or something.
From: René Scharfe <hidden> Date: 2021-06-30 16:12:51
Git grep allows combining two patterns with --and. It checks and
reports if the second pattern is missing when compiling the expression.
A missing first pattern, however, is only reported later at match time.
Thus no error is returned if no matching is done, e.g. because no file
matches the also given pathspec.
When that happens we get an expression tree with an GREP_NODE_AND node
and a NULL pointer to the missing left child. free_pattern_expr()
tries to dereference it during the cleanup at the end, which results
in a segmentation fault.
Fix this by verifying the presence of the left operand at expression
compilation time.
Reported-by: Matthew Hughes <redacted>
Helped-by: Ævar Arnfjörð Bjarmason [off-list ref]
Signed-off-by: René Scharfe <redacted>
---
Changes since v2:
- more specific error message,
- grammar error fix in commit message,
- no test of already working behavior.
grep.c | 2 ++
t/t7810-grep.sh | 9 +++++++++
2 files changed, 11 insertions(+)
@@ -657,6 +657,8 @@ static struct grep_expr *compile_pattern_and(struct grep_pat **list)x=compile_pattern_not(list);p=*list;if(p&&p->token==GREP_AND){+if(!x)+die("--and not preceded by pattern expression");if(!p->next)die("--and not followed by pattern expression");*list=p->next;
@@ -89,6 +96,8 @@ test_expect_success 'grep should not segfault with a bad input' 'test_must_failgitgrep"("'+test_invalid_grep_expression--and-eA+forHinHEAD''docase"$H"in--