Skip to content

Commit 42f9852

Browse files
committed
common_prefix: simplify and fix scanning for prefixes
common_prefix() scans backwards from the far end of each 'next' pathspec, starting from 'len', shortening the 'prefix' using 'path' as a reference. However, there is a small opportunity for an out-of-bounds access because len is unconditionally set to prefix-1 after a "direct match" test failed. This means that if 'next' is shorter than prefix+2, we read past it. Instead of a minimal fix, simplify the loop: scan *forward* over the 'next' entry, remembering the last '/' where it matched the prefix known so far. This is far easier to read and also has the advantage that we only scan over each entry once. Acked-by: Thomas Rast <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e0a9110 commit 42f9852

File tree

1 file changed

+13
-13
lines changed

1 file changed

+13
-13
lines changed

dir.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,22 @@ static int common_prefix(const char **pathspec)
3131
if (!slash)
3232
return 0;
3333

34+
/*
35+
* The first 'prefix' characters of 'path' are common leading
36+
* path components among the pathspecs we have seen so far,
37+
* including the trailing slash.
38+
*/
3439
prefix = slash - path + 1;
3540
while ((next = *++pathspec) != NULL) {
36-
int len = strlen(next);
37-
if (len >= prefix && !memcmp(path, next, prefix))
41+
int len, last_matching_slash = -1;
42+
for (len = 0; len < prefix && next[len] == path[len]; len++)
43+
if (next[len] == '/')
44+
last_matching_slash = len;
45+
if (len == prefix)
3846
continue;
39-
len = prefix - 1;
40-
for (;;) {
41-
if (!len)
42-
return 0;
43-
if (next[--len] != '/')
44-
continue;
45-
if (memcmp(path, next, len+1))
46-
continue;
47-
prefix = len + 1;
48-
break;
49-
}
47+
if (last_matching_slash < 0)
48+
return 0;
49+
prefix = last_matching_slash + 1;
5050
}
5151
return prefix;
5252
}

0 commit comments

Comments
 (0)