line-range: fix infinite loop bug with '$' regex

When the -L argument to "git log" is passed the zero-width regular
expression "$" (as in "-L :$:line-range.c"), this results in an
infinite loop in find_funcname_matching_regexp().

Modify find_funcname_matching_regexp to correctly match the entire line
instead of the zero-width match at eol and update the loop condition to
prevent an infinite loop in the event of other undiscovered corner cases.

The primary change is that we pre-decrement the beginning-of-line marker
('bol') before comparing it to '\n'. In the case of '$', where we match the
'\n' at the end of the line and start the loop with bol == eol, this
ensures that bol will find the beginning of the line on which the match
occurred.

Originally reported in <https://stackoverflow.com/q/74690545/147356>.

Signed-off-by: Lars Kellogg-Stedman <lars@oddbit.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Lars Kellogg-Stedman 2022-12-19 17:48:50 -05:00 committed by Junio C Hamano
parent 8706a59933
commit 4e57c88e02
2 changed files with 26 additions and 3 deletions

View file

@ -135,7 +135,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
{ {
int reg_error; int reg_error;
regmatch_t match[1]; regmatch_t match[1];
while (1) { while (*start) {
const char *bol, *eol; const char *bol, *eol;
reg_error = regexec(regexp, start, 1, match, 0); reg_error = regexec(regexp, start, 1, match, 0);
if (reg_error == REG_NOMATCH) if (reg_error == REG_NOMATCH)
@ -148,8 +148,8 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
/* determine extent of line matched */ /* determine extent of line matched */
bol = start+match[0].rm_so; bol = start+match[0].rm_so;
eol = start+match[0].rm_eo; eol = start+match[0].rm_eo;
while (bol > start && *bol != '\n') while (bol > start && *--bol != '\n')
bol--; ; /* nothing */
if (*bol == '\n') if (*bol == '\n')
bol++; bol++;
while (*eol && *eol != '\n') while (*eol && *eol != '\n')
@ -161,6 +161,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
return bol; return bol;
start = eol; start = eol;
} }
return NULL;
} }
static const char *parse_range_funcname( static const char *parse_range_funcname(

View file

@ -315,4 +315,26 @@ test_expect_success 'line-log with --before' '
test_cmp expect actual test_cmp expect actual
' '
test_expect_success 'setup tests for zero-width regular expressions' '
cat >expect <<-EOF
Modify func1() in file.c
Add func1() and func2() in file.c
EOF
'
test_expect_success 'zero-width regex $ matches any function name' '
git log --format="%s" --no-patch "-L:$:file.c" >actual &&
test_cmp expect actual
'
test_expect_success 'zero-width regex ^ matches any function name' '
git log --format="%s" --no-patch "-L:^:file.c" >actual &&
test_cmp expect actual
'
test_expect_success 'zero-width regex .* matches any function name' '
git log --format="%s" --no-patch "-L:.*:file.c" >actual &&
test_cmp expect actual
'
test_done test_done