]> git.ipfire.org Git - thirdparty/git.git/commitdiff
refs/ref-cache: fix SEGFAULT when seeking in empty directories
authorKarthik Nayak <karthik.188@gmail.com>
Wed, 1 Oct 2025 12:17:29 +0000 (14:17 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 1 Oct 2025 20:12:24 +0000 (13:12 -0700)
The 'cache_ref_iterator_seek()' function is used to seek the
`ref_iterator` to the desired reference in the ref-cache mechanism. We
use the seeking functionality to implement the '--start-after' flag in
'git-for-each-ref(1)'.

When using the files-backend with packed-refs, it is possible that some
of the refs directories are empty. For e.g. just after repacking, the
'refs/heads' directory would be empty. The ref-cache seek mechanism,
doesn't take this into consideration when descending into a
subdirectory, and makes an out of bounds access, causing SEGFAULT as we
try to access entries within the directory. Fix this by breaking out of
the loop when we enter an empty directory.

Since we start with the base directory of 'refs/' which is never empty,
it is okay to perform this check after the first iteration in the
`do..while` clause.

Add tests which simulate this behavior and also provide coverage over
using the feature over packed-refs.

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
refs/ref-cache.c
t/t6302-for-each-ref-filter.sh

index c180e0aad74e87125ee78fe619826f4107fc55fd..e5e5df16d85e4003277664d1d87ddad4a91bd234 100644 (file)
@@ -539,7 +539,7 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
                                 */
                                break;
                        }
-               } while (slash);
+               } while (slash && dir->nr);
        }
 
        return 0;
index 9b80ea1e3b741471703ea34430c8b43d49a88232..7f060d97bf509ca1659873e7d735e867553300ae 100755 (executable)
@@ -754,4 +754,69 @@ test_expect_success 'start after used with custom sort order' '
        test_cmp expect actual
 '
 
+test_expect_success 'start after with packed refs' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               test_commit default &&
+
+               git update-ref --stdin <<-\EOF &&
+               create refs/heads/branch @
+               create refs/heads/side @
+               create refs/odd/spot @
+               create refs/tags/one @
+               create refs/tags/two @
+               commit
+               EOF
+
+               cat >expect <<-\EOF &&
+               refs/tags/default
+               refs/tags/one
+               refs/tags/two
+               EOF
+
+               git pack-refs --all &&
+               git for-each-ref --format="%(refname)" --start-after=refs/odd/spot >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'start after with packed refs and some loose refs' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               test_commit default &&
+
+               git update-ref --stdin <<-\EOF &&
+               create refs/heads/branch @
+               create refs/heads/side @
+               create refs/odd/spot @
+               create refs/tags/one @
+               create refs/tags/two @
+               commit
+               EOF
+
+               git pack-refs --all &&
+
+               git update-ref --stdin <<-\EOF &&
+               create refs/heads/foo @
+               create refs/odd/tee @
+               commit
+               EOF
+
+               cat >expect <<-\EOF &&
+               refs/odd/tee
+               refs/tags/default
+               refs/tags/one
+               refs/tags/two
+               EOF
+
+
+               git for-each-ref --format="%(refname)" --start-after=refs/odd/spot >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done