Skip to content

Commit db84376

Browse files
avargitster
authored andcommitted
grep.c: remove "extended" in favor of "pattern_expression", fix segfault
Since 79d3696 (git-grep: boolean expression on pattern matching., 2006-06-30) the "pattern_expression" member has been used for complex queries (AND/OR...), with "pattern_list" being used for the simple OR queries. Since then we've used both "pattern_expression" and its associated boolean "extended" member to see if we have a complex expression. Since f41fb66 (revisions API: have release_revisions() release "grep_filter", 2022-04-13) we've had a subtle bug relating to that: If we supplied options that were only used for "complex queries", but didn't supply the query itself we'd set "opt->extended", but would have a NULL "pattern_expression". As a result these would segfault as we tried to call "free_grep_patterns()" from "release_revisions()": git -P log -1 --invert-grep git -P log -1 --all-match The root cause of this is that we were conflating the state management we needed in "compile_grep_patterns()" itself with whether or not we had an "opt->pattern_expression" later on. In this cases as we're going through "compile_grep_patterns()" we have no "opt->pattern_list" but have "opt->no_body_match" or "opt->all_match". So we'd set "opt->extended = 1", but not "return" on "opt->extended" as that's an "else if" in the same "if" statement. That behavior is intentional and required, as the common case is that we have an "opt->pattern_list" that we're about to parse into the "opt->pattern_expression". But we don't need to keep track of this "extended" flag beyond the state management in compile_grep_patterns() itself. It needs it, but once we're out of that function we can rely on "opt->pattern_expression" being non-NULL instead for using these extended patterns. As 79d3696 itself shows we've assumed that there's a one-to-one mapping between the two since the very beginning. I.e. "match_line()" would check "opt->extended" to see if it should call "match_expr()", and the first thing we do in that function is assume that we have a "opt->pattern_expression". We'd then call "match_expr_eval()", which would have died if that "opt->pattern_expression" was NULL. The "die" was added in c922b01 (grep: fix segfault when "git grep '('" is given, 2009-04-27), and can now be removed as it's now clearly unreachable. We still do the right thing in the case that prompted that fix: git grep '(' fatal: unmatched parenthesis Arguably neither the "--invert-grep" option added in [1] nor the earlier "--all-match" option added in [2] were intended to be used stand-alone, and another approach[3] would be to error out in those cases. But since we've been treating them as a NOOP when given without --grep for a long time let's keep doing that. We could also return in "free_pattern_expr()" if the argument is non-NULL, as an alternative fix for this segfault does [4]. That would be more elegant in making the "free_*()" function behave like "free()", but it would also remove a sanity check: The "free_pattern_expr()" function calls itself recursively, and only the top-level is allowed to be NULL, let's not conflate those two conditions. 1. 22dfa8a (log: teach --invert-grep option, 2015-01-12) 2. 0ab7bef (grep --all-match, 2006-09-27) 3. https://lore.kernel.org/git/[email protected]/ 4. http://lore.kernel.org/git/7e094882c2a71894416089f894557a9eae07e8f8.1665423686.git.me@ttaylorr.com Reported-by: orygaw <[email protected]> Signed-off-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent fd59c5b commit db84376

File tree

3 files changed

+16
-9
lines changed

3 files changed

+16
-9
lines changed

grep.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,7 @@ void compile_grep_patterns(struct grep_opt *opt)
708708
{
709709
struct grep_pat *p;
710710
struct grep_expr *header_expr = prep_header_patterns(opt);
711+
int extended = 0;
711712

712713
for (p = opt->pattern_list; p; p = p->next) {
713714
switch (p->token) {
@@ -717,14 +718,14 @@ void compile_grep_patterns(struct grep_opt *opt)
717718
compile_regexp(p, opt);
718719
break;
719720
default:
720-
opt->extended = 1;
721+
extended = 1;
721722
break;
722723
}
723724
}
724725

725726
if (opt->all_match || opt->no_body_match || header_expr)
726-
opt->extended = 1;
727-
else if (!opt->extended)
727+
extended = 1;
728+
else if (!extended)
728729
return;
729730

730731
p = opt->pattern_list;
@@ -790,7 +791,7 @@ void free_grep_patterns(struct grep_opt *opt)
790791
free(p);
791792
}
792793

793-
if (!opt->extended)
794+
if (!opt->pattern_expression)
794795
return;
795796
free_pattern_expr(opt->pattern_expression);
796797
}
@@ -971,8 +972,6 @@ static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x,
971972
{
972973
int h = 0;
973974

974-
if (!x)
975-
die("Not a valid grep expression");
976975
switch (x->node) {
977976
case GREP_NODE_TRUE:
978977
h = 1;
@@ -1052,7 +1051,7 @@ static int match_line(struct grep_opt *opt,
10521051
struct grep_pat *p;
10531052
int hit = 0;
10541053

1055-
if (opt->extended)
1054+
if (opt->pattern_expression)
10561055
return match_expr(opt, bol, eol, ctx, col, icol,
10571056
collect_hits);
10581057

@@ -1370,7 +1369,7 @@ static int should_lookahead(struct grep_opt *opt)
13701369
{
13711370
struct grep_pat *p;
13721371

1373-
if (opt->extended)
1372+
if (opt->pattern_expression)
13741373
return 0; /* punt for too complex stuff */
13751374
if (opt->invert)
13761375
return 0;

grep.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ struct grep_opt {
151151
#define GREP_BINARY_TEXT 2
152152
int binary;
153153
int allow_textconv;
154-
int extended;
155154
int use_reflog_filter;
156155
int relative;
157156
int pathname;

t/t4202-log.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ test_expect_success 'log --grep' '
249249
test_cmp expect actual
250250
'
251251

252+
for noop_opt in --invert-grep --all-match
253+
do
254+
test_expect_success "log $noop_opt without --grep is a NOOP" '
255+
git log >expect &&
256+
git log $noop_opt >actual &&
257+
test_cmp expect actual
258+
'
259+
done
260+
252261
cat > expect << EOF
253262
second
254263
initial

0 commit comments

Comments
 (0)