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>

Lars Kellogg-Stedman committed Dec 19, 2022 at 17:48 UTC 4e57c88e028c25ee9428b51ff829299bbb88a80c
2 files changed +26 -3
line-range.c
+4 -3
@@ -135,7 +135,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
135 {
136 int reg_error;
137 regmatch_t match[1];
138 - while (1) {
138 + while (*start) {
139 const char *bol, *eol;
140 reg_error = regexec(regexp, start, 1, match, 0);
141 if (reg_error == REG_NOMATCH)
@@ -148,8 +148,8 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
148 /* determine extent of line matched */
149 bol = start+match[0].rm_so;
150 eol = start+match[0].rm_eo;
151 - while (bol > start && *bol != '\n')
152 - bol--;
151 + while (bol > start && *--bol != '\n')
152 + ; /* nothing */
153 if (*bol == '\n')
154 bol++;
155 while (*eol && *eol != '\n')
@@ -161,6 +161,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
161 return bol;
162 start = eol;
163 }
164 + return NULL;
165 }
166
167 static const char *parse_range_funcname(
t/t4211-line-log.sh
+22
@@ -315,4 +315,26 @@ test_expect_success 'line-log with --before' '
315 test_cmp expect actual
316 '
317
318 +test_expect_success 'setup tests for zero-width regular expressions' '
319 + cat >expect <<-EOF
320 + Modify func1() in file.c
321 + Add func1() and func2() in file.c
322 + EOF
323 +'
324 +
325 +test_expect_success 'zero-width regex $ matches any function name' '
326 + git log --format="%s" --no-patch "-L:$:file.c" >actual &&
327 + test_cmp expect actual
328 +'
329 +
330 +test_expect_success 'zero-width regex ^ matches any function name' '
331 + git log --format="%s" --no-patch "-L:^:file.c" >actual &&
332 + test_cmp expect actual
333 +'
334 +
335 +test_expect_success 'zero-width regex .* matches any function name' '
336 + git log --format="%s" --no-patch "-L:.*:file.c" >actual &&
337 + test_cmp expect actual
338 +'
339 +
340 test_done