packfile: detect overflow in .idx file size checks

In load_idx(), we check that the .idx file is sized appropriately for
the number of objects it claims to have. We recently fixed the case
where the number of objects caused our expected size to overflow a
32-bit unsigned int, and we switched to size_t.

On a 64-bit system, this is fine; our size_t covers any expected size.
On a 32-bit system, though, it won't. The file may claim to have 2^31
objects, which will overflow even a size_t.

This doesn't hurt us at all for a well-formed idx file. A 32-bit system
would already have failed to mmap such a file, since it would be too
big. But an .idx file which _claims_ to have 2^31 objects but is
actually much smaller would fool our check.

This is a broken file, and for the most part we don't care that much
what happens. But:

  - it's a little friendlier to notice up front "woah, this file is
    broken" than it is to get nonsense results

  - later access of the data assumes that the loading function
    sanity-checked that we have at least enough bytes for the regular
    object-id table. A malformed .idx file could lead to an
    out-of-bounds read.

So let's use our overflow-checking functions to make sure that we're not
fooled by a malformed file.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jeff King 2020-11-13 00:07:19 -05:00 committed by Junio C Hamano
parent 9bb4542b8c
commit 81c4c5cf2e

View File

@ -148,7 +148,7 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
* - hash of the packfile * - hash of the packfile
* - file checksum * - file checksum
*/ */
if (idx_size != 4 * 256 + (size_t)nr * (hashsz + 4) + hashsz + hashsz) if (idx_size != st_add(4 * 256 + hashsz + hashsz, st_mult(nr, hashsz + 4)))
return error("wrong index v1 file size in %s", path); return error("wrong index v1 file size in %s", path);
} else if (version == 2) { } else if (version == 2) {
/* /*
@ -164,10 +164,10 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
* variable sized table containing 8-byte entries * variable sized table containing 8-byte entries
* for offsets larger than 2^31. * for offsets larger than 2^31.
*/ */
size_t min_size = 8 + 4*256 + (size_t)nr*(hashsz + 4 + 4) + hashsz + hashsz; size_t min_size = st_add(8 + 4*256 + hashsz + hashsz, st_mult(nr, hashsz + 4 + 4));
size_t max_size = min_size; size_t max_size = min_size;
if (nr) if (nr)
max_size += ((size_t)nr - 1)*8; max_size = st_add(max_size, st_mult(nr - 1, 8));
if (idx_size < min_size || idx_size > max_size) if (idx_size < min_size || idx_size > max_size)
return error("wrong index v2 file size in %s", path); return error("wrong index v2 file size in %s", path);
if (idx_size != min_size && if (idx_size != min_size &&