git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v4 0/2] dir: fix pathspec prefixes with exclusions

From
Yannik Tausch <dev@ytausch.de>
Date
Sep 14, 2026, 07:24 UTC
Message-ID
<7CB757FB-1F2D-4EE6-8C31-8C2CD6D42397@ytausch.de>
In-Reply-To
<886A25E6-8854-4AF6-BF0B-CFB57B673026@ytausch.de>

Pathspec prefix optimization must account for exclude items separately. The prefix is derived from non-exclude items, so applying it while matching an exclude item can compare the wrong portions of the paths. Conversely, an exclude item at the start of the pathspec currently prevents finding a common prefix among the remaining items.

The first patch matches exclude items against the full pathname. The second patch finds the common prefix starting with the first non-exclude item and returns both the prefix length and the string from which it was derived.

Changes since v3, which was withdrawn in favor of v2:
* Return to a two-patch series based on d66ac2af30, leaving Junio's
  preparatory const-correctness patch on its separately queued topic.
* Add the deterministic regression test suggested by Elijah, while
  retaining the shorter-pattern test for the out-of-bounds access.
* Explain the observable incorrect match in patch 1 and use consistent
  non-exclude/exclude terminology.
* Reword patch 2 to describe the directory-walk optimization it restores.
Yannik Tausch (2):
  dir: do not apply prefix to negative pathspecs
  dir: preserve pathspec prefix optimization with leading excludes
 dir.c                       | 39 +++++++++++++++++++++----------------
 t/t6132-pathspec-exclude.sh | 18 +++++++++++++++++
 t/unit-tests/u-dir.c        | 28 ++++++++++++++++++++++++++
 3 files changed, 68 insertions(+), 17 deletions(-)
Range-diff against v2:
1:  c8a2f1e22e ! 1:  adeb7f2fb6 dir: do not apply prefix to negative pathspecs
    @@ Metadata
      ## Commit message ##
         dir: do not apply prefix to negative pathspecs
     
    -    common_prefix_len() derives the common prefix solely from positive
    -    pathspecs, skipping those marked with PATHSPEC_EXCLUDE. However,
    -    match_pathspec_with_flags() also passes that prefix when matching the
    -    negative pathspecs.
    +    common_prefix_len() derives the common prefix solely from non-exclude
    +    pathspec items. However, match_pathspec_with_flags() also passes that
    +    prefix when matching exclude items.
     
    -    A negative pathspec may be shorter than the prefix. In that case,
    -    match_pathspec_item() advances item->match beyond its allocation and
    -    subtracts the prefix from item->len, producing a negative matchlen. It
    -    then dereferences the out-of-bounds pointer. If the resulting byte is
    -    not NUL, matchlen is converted to size_t when passed to ps_strncmp(),
    -    which may cause a much larger out-of-bounds read.
    +    This can produce incorrect results because that prefix does not
    +    necessarily match an exclude item. For example, given non-exclude items
    +    "a/b" and "a/c" and an exclude item "x/b", stripping the two-byte
    +    prefix from both the pathname "a/b/m" and pattern "x/b" makes the
    +    remaining strings match and incorrectly excludes the pathname.
     
    -    The problem can be reproduced with AddressSanitizer:
    +    If an exclude item is shorter than the prefix, match_pathspec_item()
    +    instead advances item->match beyond its allocation and subtracts the
    +    prefix from item->len, producing a negative matchlen. It then
    +    dereferences the out-of-bounds pointer. If the resulting byte is not
    +    NUL, matchlen is converted to size_t when passed to ps_strncmp(), which
    +    may cause a much larger out-of-bounds read.
    +
    +    The out-of-bounds access can be reproduced with AddressSanitizer:
     
             make SANITIZE=address CFLAGS="-g -O0" git
             git init test &&
    @@ Commit message
             git commit -m test &&
             ../git ls-files -- "$DIR/" ":(exclude)xy"
     
    -    This reports a heap-buffer-overflow. Without AddressSanitizer, the
    -    output may depend on the contents of memory following the negative
    -    pathspec.
    -
    -    Fix the bug by using a zero prefix when matching negative pathspecs.
    -    Add a regression test that combines a positive pathspec with a longer
    -    common prefix and a shorter, unrelated negative pathspec.
    +    Fix the bug by using a zero prefix when matching exclude items. Add
    +    regression tests for both the deterministic incorrect match and the
    +    shorter exclude item that causes the out-of-bounds access.
     
         Signed-off-by: Yannik Tausch <dev@ytausch.de>
     
    @@ t/t6132-pathspec-exclude.sh: EOF
     +	EOF
     +	test_cmp expect actual
     +'
    ++
    ++test_expect_success 'exclude is matched against the full path' '
    ++	git ls-files -- sub/sub/ ":(exclude)zzzzzzz" >actual &&
    ++	cat <<-\EOF >expect &&
    ++	sub/sub/file
    ++	sub/sub/sub/file
    ++	EOF
    ++	test_cmp expect actual
    ++'
     +
      test_expect_success 'multiple exclusions' '
      	git ls-files -- ":^*/file2" ":^sub2" >actual &&
2:  d0e08fdb96 ! 2:  e8f72cab9c dir: find common prefix among non-exclude pathspec items
    @@ Metadata
     Author: Yannik Tausch <dev@ytausch.de>
     
      ## Commit message ##
    -    dir: find common prefix among non-exclude pathspec items
    +    dir: preserve pathspec prefix optimization with leading excludes
     
    -    common_prefix_len() skips exclude pathspec items, but uses n == 0 to
    -    identify the initial item and items[0] as the comparison source. When
    -    an exclude item comes first, the function returns zero even when all
    -    remaining items share a directory.
    +    Directory walks use the common directory prefix of non-exclude
    +    pathspec items to avoid scanning unrelated portions of the working
    +    tree or index. Exclude items only remove paths from that candidate
    +    set, so they do not need to widen the traversal.
     
    -    Track the first non-exclude item explicitly. Return its match through
    -    an output parameter so that common_prefix() and fill_directory() use
    -    the correct string. Add a unit test with an unrelated exclude item
    -    before two non-exclude items that share a directory.
    +    When an exclude item is the first pathspec item, common_prefix_len()
    +    fails to establish a comparison base and returns a zero-length prefix.
    +    The result is correct, but Git unnecessarily traverses from a broader
    +    starting point even when all non-exclude items share a directory.
    +
    +    Use the first non-exclude item as the comparison base and return its
    +    string together with the prefix length, allowing callers to start from
    +    the recovered directory prefix. Exclude matching continues to use full
    +    paths, so this restores the optimization without changing which paths
    +    are selected. Add a unit test covering an exclude item before two
    +    non-exclude items with a common directory.
     
         Signed-off-by: Yannik Tausch <dev@ytausch.de>
     
base-commit: d66ac2af300f33bd9e8558c5645f2a808cc01f89
-- 
2.55.0
Previous: Yannik TauschNext: Yannik Tausch
Message 25 of 29 in “dir: find common prefix among positive pathspecs”
  1. dir: find common prefix among positive pathspecsYannik Tausch, Sep 2, 2026
  2. Junio C HamanoSep 2, 2026
  3. Yannik TauschSep 3, 2026
  4. 0/2 dir: fix pathspec prefixes with exclusionsYannik Tausch, Sep 3, 2026
  5. 1/2 dir: do not apply prefix to negative pathspecsYannik Tausch, Sep 3, 2026
  6. Elijah NewrenSep 4, 2026
  7. Junio C HamanoSep 4, 2026
  8. 2/2 dir: find common prefix among non-exclude pathspec itemsYannik Tausch, Sep 3, 2026
  9. Junio C HamanoSep 3, 2026
  10. pathspec: match and original in pathspec_item are constJunio C Hamano, Sep 3, 2026
  11. Yannik TauschSep 3, 2026
  12. Junio C HamanoSep 3, 2026
  13. Yannik TauschSep 3, 2026
  14. Junio C HamanoSep 3, 2026
  15. Yannik TauschSep 3, 2026
  16. Elijah NewrenSep 4, 2026
  17. Junio C HamanoSep 4, 2026
  18. Elijah NewrenSep 4, 2026
  19. Junio C HamanoSep 5, 2026
  20. Yannik TauschSep 3, 2026
  21. 0/3 dir: fix pathspec prefixes with exclusionsYannik Tausch, Sep 3, 2026
  22. 1/3 pathspec: match and original in pathspec_item are constYannik Tausch, Sep 3, 2026
  23. 2/3 dir: do not apply prefix to negative pathspecsYannik Tausch, Sep 3, 2026
  24. 3/3 dir: find common prefix among non-exclude pathspec itemsYannik Tausch, Sep 3, 2026
  25. 0/2 dir: fix pathspec prefixes with exclusionsYannik Tausch, Sep 14, 2026
  26. 1/2 dir: do not apply prefix to negative pathspecsYannik Tausch, Sep 14, 2026
  27. 2/2 dir: preserve pathspec prefix optimization with leading excludesYannik Tausch, Sep 14, 2026
  28. Junio C HamanoSep 16, 2026
  29. Yannik TauschSep 3, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.