@matheusbsbs / matheus-bagliano / commits / 42f9852f3c
Fork 0

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 <trast@student.ethz.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jun 16, 2010 at 01:02 UTC 42f9852f3c7476b5608ad297443e6d459516f8c0
1 file changed +13 -13
dir.c
+13 -13
@@ -31,22 +31,22 @@ static int common_prefix(const char **pathspec)
31 if (!slash)
32 return 0;
33
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 + */
39 prefix = slash - path + 1;
40 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)
46 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;
50 }
51 return prefix;
52 }