t / t5313-pack-bounds-checks.shon commit parse-options: drop OPT_DATE() (0a8a16a)
   1#!/bin/sh
   2
   3test_description='bounds-checking of access to mmapped on-disk file formats'
   4. ./test-lib.sh
   5
   6clear_base () {
   7        test_when_finished 'restore_base' &&
   8        rm -f $base
   9}
  10
  11restore_base () {
  12        cp base-backup/* .git/objects/pack/
  13}
  14
  15do_pack () {
  16        pack_objects=$1; shift
  17        sha1=$(
  18                for i in $pack_objects
  19                do
  20                        echo $i
  21                done | git pack-objects "$@" .git/objects/pack/pack
  22        ) &&
  23        pack=.git/objects/pack/pack-$sha1.pack &&
  24        idx=.git/objects/pack/pack-$sha1.idx &&
  25        chmod +w $pack $idx &&
  26        test_when_finished 'rm -f "$pack" "$idx"'
  27}
  28
  29munge () {
  30        printf "$3" | dd of="$1" bs=1 conv=notrunc seek=$2
  31}
  32
  33# Offset in a v2 .idx to its initial and extended offset tables. For an index
  34# with "nr" objects, this is:
  35#
  36#   magic(4) + version(4) + fan-out(4*256) + sha1s(20*nr) + crc(4*nr),
  37#
  38# for the initial, and another ofs(4*nr) past that for the extended.
  39#
  40ofs_table () {
  41        echo $((4 + 4 + 4*256 + 20*$1 + 4*$1))
  42}
  43extended_table () {
  44        echo $(($(ofs_table "$1") + 4*$1))
  45}
  46
  47test_expect_success 'set up base packfile and variables' '
  48        # the hash of this content starts with ff, which
  49        # makes some later computations much simpler
  50        echo 74 >file &&
  51        git add file &&
  52        git commit -m base &&
  53        git repack -ad &&
  54        base=$(echo .git/objects/pack/*) &&
  55        chmod +w $base &&
  56        mkdir base-backup &&
  57        cp $base base-backup/ &&
  58        object=$(git rev-parse HEAD:file)
  59'
  60
  61test_expect_success 'pack/index object count mismatch' '
  62        do_pack $object &&
  63        munge $pack 8 "\377\0\0\0" &&
  64        clear_base &&
  65
  66        # We enumerate the objects from the completely-fine
  67        # .idx, but notice later that the .pack is bogus
  68        # and fail to show any data.
  69        echo "$object missing" >expect &&
  70        git cat-file --batch-all-objects --batch-check >actual &&
  71        test_cmp expect actual &&
  72
  73        # ...and here fail to load the object (without segfaulting),
  74        # but fallback to a good copy if available.
  75        test_must_fail git cat-file blob $object &&
  76        restore_base &&
  77        git cat-file blob $object >actual &&
  78        test_cmp file actual &&
  79
  80        # ...and make sure that index-pack --verify, which has its
  81        # own reading routines, does not segfault.
  82        test_must_fail git index-pack --verify $pack
  83'
  84
  85test_expect_success 'matched bogus object count' '
  86        do_pack $object &&
  87        munge $pack 8 "\377\0\0\0" &&
  88        munge $idx $((255 * 4)) "\377\0\0\0" &&
  89        clear_base &&
  90
  91        # Unlike above, we should notice early that the .idx is totally
  92        # bogus, and not even enumerate its contents.
  93        git cat-file --batch-all-objects --batch-check >actual &&
  94        test_must_be_empty actual &&
  95
  96        # But as before, we can do the same object-access checks.
  97        test_must_fail git cat-file blob $object &&
  98        restore_base &&
  99        git cat-file blob $object >actual &&
 100        test_cmp file actual &&
 101
 102        test_must_fail git index-pack --verify $pack
 103'
 104
 105# Note that we cannot check the fallback case for these
 106# further .idx tests, as we notice the problem in functions
 107# whose interface doesn't allow an error return (like use_pack()),
 108# and thus we just die().
 109#
 110# There's also no point in doing enumeration tests, as
 111# we are munging offsets here, which are about looking up
 112# specific objects.
 113
 114test_expect_success 'bogus object offset (v1)' '
 115        do_pack $object --index-version=1 &&
 116        munge $idx $((4 * 256)) "\377\0\0\0" &&
 117        clear_base &&
 118        test_must_fail git cat-file blob $object &&
 119        test_must_fail git index-pack --verify $pack
 120'
 121
 122test_expect_success 'bogus object offset (v2, no msb)' '
 123        do_pack $object --index-version=2 &&
 124        munge $idx $(ofs_table 1) "\0\377\0\0" &&
 125        clear_base &&
 126        test_must_fail git cat-file blob $object &&
 127        test_must_fail git index-pack --verify $pack
 128'
 129
 130test_expect_success 'bogus offset into v2 extended table' '
 131        do_pack $object --index-version=2 &&
 132        munge $idx $(ofs_table 1) "\377\0\0\0" &&
 133        clear_base &&
 134        test_must_fail git cat-file blob $object &&
 135        test_must_fail git index-pack --verify $pack
 136'
 137
 138test_expect_success 'bogus offset inside v2 extended table' '
 139        # We need two objects here, so we can plausibly require
 140        # an extended table (if the first object were larger than 2^31).
 141        #
 142        # Note that the value is important here. We want $object as
 143        # the second entry in sorted-sha1 order. The sha1 of 1485 starts
 144        # with "000", which sorts before that of $object (which starts
 145        # with "fff").
 146        second=$(echo 1485 | git hash-object -w --stdin) &&
 147        do_pack "$object $second" --index-version=2 &&
 148
 149        # We have to make extra room for the table, so we cannot
 150        # just munge in place as usual.
 151        {
 152                dd if=$idx bs=1 count=$(($(ofs_table 2) + 4)) &&
 153                printf "\200\0\0\0" &&
 154                printf "\377\0\0\0\0\0\0\0" &&
 155                dd if=$idx bs=1 skip=$(extended_table 2)
 156        } >tmp &&
 157        mv tmp "$idx" &&
 158        clear_base &&
 159        test_must_fail git cat-file blob $object &&
 160        test_must_fail git index-pack --verify $pack
 161'
 162
 163test_expect_success 'bogus OFS_DELTA in packfile' '
 164        # Generate a pack with a delta in it.
 165        base=$(test-tool genrandom foo 3000 | git hash-object --stdin -w) &&
 166        delta=$(test-tool genrandom foo 2000 | git hash-object --stdin -w) &&
 167        do_pack "$base $delta" --delta-base-offset &&
 168        rm -f .git/objects/??/* &&
 169
 170        # Double check that we have the delta we expect.
 171        echo $base >expect &&
 172        echo $delta | git cat-file --batch-check="%(deltabase)" >actual &&
 173        test_cmp expect actual &&
 174
 175        # Now corrupt it. We assume the varint size for the delta is small
 176        # enough to fit in the first byte (which it should be, since it
 177        # is a pure deletion from the base), and that original ofs_delta
 178        # takes 2 bytes (which it should, as it should be ~3000).
 179        ofs=$(git show-index <$idx | grep $delta | cut -d" " -f1) &&
 180        munge $pack $(($ofs + 1)) "\177\377" &&
 181        test_must_fail git cat-file blob $delta >/dev/null
 182'
 183
 184test_done