#!/bin/sh test_description='bounds-checking of access to mmapped on-disk file formats' . ./test-lib.sh clear_base () { test_when_finished 'restore_base' && rm -f $base } restore_base () { cp base-backup/* .git/objects/pack/ } do_pack () { pack_objects=$1; shift sha1=$( for i in $pack_objects do echo $i done | git pack-objects "$@" .git/objects/pack/pack ) && pack=.git/objects/pack/pack-$sha1.pack && idx=.git/objects/pack/pack-$sha1.idx && chmod +w $pack $idx && test_when_finished 'rm -f "$pack" "$idx"' } munge () { printf "$3" | dd of="$1" bs=1 conv=notrunc seek=$2 } # Offset in a v2 .idx to its initial and extended offset tables. For an index # with "nr" objects, this is: # # magic(4) + version(4) + fan-out(4*256) + sha1s(20*nr) + crc(4*nr), # # for the initial, and another ofs(4*nr) past that for the extended. # ofs_table () { echo $((4 + 4 + 4*256 + 20*$1 + 4*$1)) } extended_table () { echo $(($(ofs_table "$1") + 4*$1)) } test_expect_success 'set up base packfile and variables' ' # the hash of this content starts with ff, which # makes some later computations much simpler echo 74 >file && git add file && git commit -m base && git repack -ad && base=$(echo .git/objects/pack/*) && chmod +w $base && mkdir base-backup && cp $base base-backup/ && object=$(git rev-parse HEAD:file) ' test_expect_success 'pack/index object count mismatch' ' do_pack $object && munge $pack 8 "\377\0\0\0" && clear_base && # We enumerate the objects from the completely-fine # .idx, but notice later that the .pack is bogus # and fail to show any data. echo "$object missing" >expect && git cat-file --batch-all-objects --batch-check >actual && test_cmp expect actual && # ...and here fail to load the object (without segfaulting), # but fallback to a good copy if available. test_must_fail git cat-file blob $object && restore_base && git cat-file blob $object >actual && test_cmp file actual && # ...and make sure that index-pack --verify, which has its # own reading routines, does not segfault. test_must_fail git index-pack --verify $pack ' test_expect_success 'matched bogus object count' ' do_pack $object && munge $pack 8 "\377\0\0\0" && munge $idx $((255 * 4)) "\377\0\0\0" && clear_base && # Unlike above, we should notice early that the .idx is totally # bogus, and not even enumerate its contents. >expect && git cat-file --batch-all-objects --batch-check >actual && test_cmp expect actual && # But as before, we can do the same object-access checks. test_must_fail git cat-file blob $object && restore_base && git cat-file blob $object >actual && test_cmp file actual && test_must_fail git index-pack --verify $pack ' # Note that we cannot check the fallback case for these # further .idx tests, as we notice the problem in functions # whose interface doesn't allow an error return (like use_pack()), # and thus we just die(). # # There's also no point in doing enumeration tests, as # we are munging offsets here, which are about looking up # specific objects. test_expect_success 'bogus object offset (v1)' ' do_pack $object --index-version=1 && munge $idx $((4 * 256)) "\377\0\0\0" && clear_base && test_must_fail git cat-file blob $object && test_must_fail git index-pack --verify $pack ' test_expect_success 'bogus object offset (v2, no msb)' ' do_pack $object --index-version=2 && munge $idx $(ofs_table 1) "\0\377\0\0" && clear_base && test_must_fail git cat-file blob $object && test_must_fail git index-pack --verify $pack ' test_expect_success 'bogus offset into v2 extended table' ' do_pack $object --index-version=2 && munge $idx $(ofs_table 1) "\377\0\0\0" && clear_base && test_must_fail git cat-file blob $object && test_must_fail git index-pack --verify $pack ' test_expect_success 'bogus offset inside v2 extended table' ' # We need two objects here, so we can plausibly require # an extended table (if the first object were larger than 2^31). do_pack "$object $(git rev-parse HEAD)" --index-version=2 && # We have to make extra room for the table, so we cannot # just munge in place as usual. { dd if=$idx bs=1 count=$(($(ofs_table 2) + 4)) && printf "\200\0\0\0" && printf "\377\0\0\0\0\0\0\0" && dd if=$idx bs=1 skip=$(extended_table 2) } >tmp && mv tmp "$idx" && clear_base && test_must_fail git cat-file blob $object && test_must_fail git index-pack --verify $pack ' test_expect_success 'bogus OFS_DELTA in packfile' ' # Generate a pack with a delta in it. base=$(test-genrandom foo 3000 | git hash-object --stdin -w) && delta=$(test-genrandom foo 2000 | git hash-object --stdin -w) && do_pack "$base $delta" --delta-base-offset && rm -f .git/objects/??/* && # Double check that we have the delta we expect. echo $base >expect && echo $delta | git cat-file --batch-check="%(deltabase)" >actual && test_cmp expect actual && # Now corrupt it. We assume the varint size for the delta is small # enough to fit in the first byte (which it should be, since it # is a pure deletion from the base), and that original ofs_delta # takes 2 bytes (which it should, as it should be ~3000). ofs=$(git show-index <$idx | grep $delta | cut -d" " -f1) && munge $pack $(($ofs + 1)) "\177\377" && test_must_fail git cat-file blob $delta >/dev/null ' test_done