]> git.ipfire.org Git - thirdparty/git.git/commitdiff
pathspec: use match for sparse-index expansion checks
authorTed Nyman <tnyman@openai.com>
Mon, 20 Jul 2026 22:31:20 +0000 (15:31 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 21 Jul 2026 17:33:14 +0000 (10:33 -0700)
The pathspec parser computes `len` and `nowildcard_len` from
`item.match`, which includes any prefix added when a command is run
from a subdirectory. `item.original` can still contain the shorter,
unprefixed argument.

Using `item.original + item.nowildcard_len` in
`pathspec_needs_expanded_index()` can therefore read past the end of
the allocation. AddressSanitizer reports a heap-buffer-overflow for
prefixed wildcard pathspecs passed to `git rm` and `git reset` with a
sparse index.

The mismatch dates back to 4d1cfc1351 ("reset: make --mixed
sparse-aware", 2021-11-29), which introduced the helper using
`item.original`. b29ad38322 ("pathspec.h: move
pathspec_needs_expanded_index() from reset.c to here", 2022-08-07)
later moved it to `pathspec.c` and preserved the affected comparisons.

Use `item.match` consistently when checking whether a pathspec can
match a sparse-directory entry. Add coverage for prefixed wildcard
pathspecs so both commands keep the index sparse.

Signed-off-by: Ted Nyman <tnyman@openai.com>
Reviewed-by: Taylor Blau <ttaylorr@openai.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
pathspec.c
t/t1092-sparse-checkout-compatibility.sh

index f78b22709ccb673873d636b1b08f412c18a5f435..281858f21f9c59839b707e04a2f2ee113389b2a3 100644 (file)
@@ -847,9 +847,9 @@ int pathspec_needs_expanded_index(struct index_state *istate,
                         * - not-in-cone/bar*: may need expanded index
                         * - **.c: may need expanded index
                         */
-                       if (strspn(item.original + item.nowildcard_len, "*") ==
+                       if (strspn(item.match + item.nowildcard_len, "*") ==
                                    (unsigned int)(item.len - item.nowildcard_len) &&
-                           path_in_cone_mode_sparse_checkout(item.original, istate))
+                           path_in_cone_mode_sparse_checkout(item.match, istate))
                                continue;
 
                        for (pos = 0; pos < istate->cache_nr; pos++) {
@@ -865,7 +865,7 @@ int pathspec_needs_expanded_index(struct index_state *istate,
                                 */
                                if ((unsigned int)item.nowildcard_len >
                                            ce_namelen(ce) &&
-                                   !strncmp(item.original, ce->name,
+                                   !strncmp(item.match, ce->name,
                                             ce_namelen(ce))) {
                                        res = 1;
                                        break;
@@ -876,13 +876,13 @@ int pathspec_needs_expanded_index(struct index_state *istate,
                                 * directory and the pathspec does not match the whole
                                 * directory, need to expand the index.
                                 */
-                               if (!strncmp(item.original, ce->name, item.nowildcard_len) &&
-                                   wildmatch(item.original, ce->name, 0)) {
+                               if (!strncmp(item.match, ce->name, item.nowildcard_len) &&
+                                   wildmatch(item.match, ce->name, 0)) {
                                        res = 1;
                                        break;
                                }
                        }
-               } else if (!path_in_cone_mode_sparse_checkout(item.original, istate) &&
+               } else if (!path_in_cone_mode_sparse_checkout(item.match, istate) &&
                           !matches_skip_worktree(pathspec, i, &skip_worktree_seen))
                        res = 1;
 
index 8186da5c887c56301fb0b2a3ec731b99091bdec9..d4a9c0bbf2d0fdc8fdcdc814b27acd3b7aa5ecbe 100755 (executable)
@@ -2119,6 +2119,13 @@ test_expect_success 'sparse index is not expanded: rm' '
        ensure_not_expanded rm -r deep
 '
 
+test_expect_success 'sparse index is not expanded: prefixed wildcard pathspec' '
+       init_repos &&
+
+       ensure_not_expanded -C deep rm --dry-run -- "a*" &&
+       ensure_not_expanded -C deep reset base -- "a*"
+'
+
 test_expect_success 'grep with and --cached' '
        init_repos &&