Skip to content

Commit a128386

Browse files
peffgitster
authored andcommitted
t5313: test bounds-checks of corrupted/malicious pack/idx files
Our on-disk .pack and .idx files may reference other data by offset. We should make sure that we are not fooled by corrupt data into accessing memory outside of our mmap'd boundaries. This patch adds a series of tests for offsets found in .pack and .idx files. For the most part we get this right, but there are two tests of .idx files marked as failures: we do not bounds-check offsets in the v2 index's extended offset table, nor do we handle .idx offsets that overflow a signed off_t. With these tests, we should have good coverage of all offsets found in these files. Note that this doesn't cover .bitmap files, which may have similar bugs. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a2558fb commit a128386

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

t/t5313-pack-bounds-checks.sh

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#!/bin/sh
2+
3+
test_description='bounds-checking of access to mmapped on-disk file formats'
4+
. ./test-lib.sh
5+
6+
clear_base () {
7+
test_when_finished 'restore_base' &&
8+
rm -f $base
9+
}
10+
11+
restore_base () {
12+
cp base-backup/* .git/objects/pack/
13+
}
14+
15+
do_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+
29+
munge () {
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+
#
40+
ofs_table () {
41+
echo $((4 + 4 + 4*256 + 20*$1 + 4*$1))
42+
}
43+
extended_table () {
44+
echo $(($(ofs_table "$1") + 4*$1))
45+
}
46+
47+
test_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+
61+
test_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+
85+
test_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+
>expect &&
94+
git cat-file --batch-all-objects --batch-check >actual &&
95+
test_cmp expect actual &&
96+
97+
# But as before, we can do the same object-access checks.
98+
test_must_fail git cat-file blob $object &&
99+
restore_base &&
100+
git cat-file blob $object >actual &&
101+
test_cmp file actual &&
102+
103+
test_must_fail git index-pack --verify $pack
104+
'
105+
106+
# Note that we cannot check the fallback case for these
107+
# further .idx tests, as we notice the problem in functions
108+
# whose interface doesn't allow an error return (like use_pack()),
109+
# and thus we just die().
110+
#
111+
# There's also no point in doing enumeration tests, as
112+
# we are munging offsets here, which are about looking up
113+
# specific objects.
114+
115+
test_expect_success 'bogus object offset (v1)' '
116+
do_pack $object --index-version=1 &&
117+
munge $idx $((4 * 256)) "\377\0\0\0" &&
118+
clear_base &&
119+
test_must_fail git cat-file blob $object &&
120+
test_must_fail git index-pack --verify $pack
121+
'
122+
123+
test_expect_success 'bogus object offset (v2, no msb)' '
124+
do_pack $object --index-version=2 &&
125+
munge $idx $(ofs_table 1) "\0\377\0\0" &&
126+
clear_base &&
127+
test_must_fail git cat-file blob $object &&
128+
test_must_fail git index-pack --verify $pack
129+
'
130+
131+
test_expect_failure 'bogus offset into v2 extended table' '
132+
do_pack $object --index-version=2 &&
133+
munge $idx $(ofs_table 1) "\377\0\0\0" &&
134+
clear_base &&
135+
test_must_fail git cat-file blob $object &&
136+
test_must_fail git index-pack --verify $pack
137+
'
138+
139+
test_expect_failure 'bogus offset inside v2 extended table' '
140+
# We need two objects here, so we can plausibly require
141+
# an extended table (if the first object were larger than 2^31).
142+
do_pack "$object $(git rev-parse HEAD)" --index-version=2 &&
143+
144+
# We have to make extra room for the table, so we cannot
145+
# just munge in place as usual.
146+
{
147+
dd if=$idx bs=1 count=$(($(ofs_table 2) + 4)) &&
148+
printf "\200\0\0\0" &&
149+
printf "\377\0\0\0\0\0\0\0" &&
150+
dd if=$idx bs=1 skip=$(extended_table 2)
151+
} >tmp &&
152+
mv tmp "$idx" &&
153+
clear_base &&
154+
test_must_fail git cat-file blob $object &&
155+
test_must_fail git index-pack --verify $pack
156+
'
157+
158+
test_expect_success 'bogus OFS_DELTA in packfile' '
159+
# Generate a pack with a delta in it.
160+
base=$(test-genrandom foo 3000 | git hash-object --stdin -w) &&
161+
delta=$(test-genrandom foo 2000 | git hash-object --stdin -w) &&
162+
do_pack "$base $delta" --delta-base-offset &&
163+
rm -f .git/objects/??/* &&
164+
165+
# Double check that we have the delta we expect.
166+
echo $base >expect &&
167+
echo $delta | git cat-file --batch-check="%(deltabase)" >actual &&
168+
test_cmp expect actual &&
169+
170+
# Now corrupt it. We assume the varint size for the delta is small
171+
# enough to fit in the first byte (which it should be, since it
172+
# is a pure deletion from the base), and that original ofs_delta
173+
# takes 2 bytes (which it should, as it should be ~3000).
174+
ofs=$(git show-index <$idx | grep $delta | cut -d" " -f1) &&
175+
munge $pack $(($ofs + 1)) "\177\377" &&
176+
test_must_fail git cat-file blob $delta >/dev/null
177+
'
178+
179+
test_done

0 commit comments

Comments
 (0)