Merge branch 'np/pack-safer'
* np/pack-safer: t5303: fix printf format string for portability t5303: work around printf breakage in dash pack-objects: don't leak pack window reference when splitting packs extend test coverage for latest pack corruption resilience improvements pack-objects: allow "fixing" a corrupted pack without a full repack make find_pack_revindex() aware of the nasty world make check_object() resilient to pack corruptions make packed_object_info() resilient to pack corruptions make unpack_object_header() non fatal better validation on delta base object offsets close another possibility for propagating pack corruption
This commit is contained in:
85
sha1_file.c
85
sha1_file.c
@ -1122,7 +1122,8 @@ static int legacy_loose_object(unsigned char *map)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep)
|
||||
unsigned long unpack_object_header_buffer(const unsigned char *buf,
|
||||
unsigned long len, enum object_type *type, unsigned long *sizep)
|
||||
{
|
||||
unsigned shift;
|
||||
unsigned char c;
|
||||
@ -1134,10 +1135,10 @@ unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned lon
|
||||
size = c & 15;
|
||||
shift = 4;
|
||||
while (c & 0x80) {
|
||||
if (len <= used)
|
||||
return 0;
|
||||
if (sizeof(long) * 8 <= shift)
|
||||
if (len <= used || sizeof(long) * 8 <= shift) {
|
||||
error("bad object header");
|
||||
return 0;
|
||||
}
|
||||
c = buf[used++];
|
||||
size += (c & 0x7f) << shift;
|
||||
shift += 7;
|
||||
@ -1176,7 +1177,7 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
|
||||
* really worth it and we don't write it any longer. But we
|
||||
* can still read it.
|
||||
*/
|
||||
used = unpack_object_header_gently(map, mapsize, &type, &size);
|
||||
used = unpack_object_header_buffer(map, mapsize, &type, &size);
|
||||
if (!used || !valid_loose_object_type[type])
|
||||
return -1;
|
||||
map += used;
|
||||
@ -1325,8 +1326,10 @@ unsigned long get_size_from_delta(struct packed_git *p,
|
||||
} while ((st == Z_OK || st == Z_BUF_ERROR) &&
|
||||
stream.total_out < sizeof(delta_head));
|
||||
inflateEnd(&stream);
|
||||
if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
|
||||
die("delta data unpack-initial failed");
|
||||
if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) {
|
||||
error("delta data unpack-initial failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Examine the initial part of the delta to figure out
|
||||
* the result size.
|
||||
@ -1367,7 +1370,7 @@ static off_t get_delta_base(struct packed_git *p,
|
||||
base_offset = (base_offset << 7) + (c & 127);
|
||||
}
|
||||
base_offset = delta_obj_offset - base_offset;
|
||||
if (base_offset >= delta_obj_offset)
|
||||
if (base_offset <= 0 || base_offset >= delta_obj_offset)
|
||||
return 0; /* out of bound */
|
||||
*curpos += used;
|
||||
} else if (type == OBJ_REF_DELTA) {
|
||||
@ -1393,15 +1396,32 @@ static int packed_delta_info(struct packed_git *p,
|
||||
off_t base_offset;
|
||||
|
||||
base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
|
||||
if (!base_offset)
|
||||
return OBJ_BAD;
|
||||
type = packed_object_info(p, base_offset, NULL);
|
||||
if (type <= OBJ_NONE) {
|
||||
struct revindex_entry *revidx;
|
||||
const unsigned char *base_sha1;
|
||||
revidx = find_pack_revindex(p, base_offset);
|
||||
if (!revidx)
|
||||
return OBJ_BAD;
|
||||
base_sha1 = nth_packed_object_sha1(p, revidx->nr);
|
||||
mark_bad_packed_object(p, base_sha1);
|
||||
type = sha1_object_info(base_sha1, NULL);
|
||||
if (type <= OBJ_NONE)
|
||||
return OBJ_BAD;
|
||||
}
|
||||
|
||||
/* We choose to only get the type of the base object and
|
||||
* ignore potentially corrupt pack file that expects the delta
|
||||
* based on a base with a wrong size. This saves tons of
|
||||
* inflate() calls.
|
||||
*/
|
||||
if (sizep)
|
||||
if (sizep) {
|
||||
*sizep = get_size_from_delta(p, w_curs, curpos);
|
||||
if (*sizep == 0)
|
||||
type = OBJ_BAD;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
@ -1423,10 +1443,11 @@ static int unpack_object_header(struct packed_git *p,
|
||||
* insane, so we know won't exceed what we have been given.
|
||||
*/
|
||||
base = use_pack(p, w_curs, *curpos, &left);
|
||||
used = unpack_object_header_gently(base, left, &type, sizep);
|
||||
if (!used)
|
||||
die("object offset outside of pack file");
|
||||
*curpos += used;
|
||||
used = unpack_object_header_buffer(base, left, &type, sizep);
|
||||
if (!used) {
|
||||
type = OBJ_BAD;
|
||||
} else
|
||||
*curpos += used;
|
||||
|
||||
return type;
|
||||
}
|
||||
@ -1510,8 +1531,9 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
|
||||
*sizep = size;
|
||||
break;
|
||||
default:
|
||||
die("pack %s contains unknown object type %d",
|
||||
p->pack_name, type);
|
||||
error("unknown object type %i at offset %"PRIuMAX" in %s",
|
||||
type, (uintmax_t)obj_offset, p->pack_name);
|
||||
type = OBJ_BAD;
|
||||
}
|
||||
unuse_pack(&w_curs);
|
||||
return type;
|
||||
@ -1675,9 +1697,12 @@ static void *unpack_delta_entry(struct packed_git *p,
|
||||
* This is costly but should happen only in the presence
|
||||
* of a corrupted pack, and is better than failing outright.
|
||||
*/
|
||||
struct revindex_entry *revidx = find_pack_revindex(p, base_offset);
|
||||
const unsigned char *base_sha1 =
|
||||
nth_packed_object_sha1(p, revidx->nr);
|
||||
struct revindex_entry *revidx;
|
||||
const unsigned char *base_sha1;
|
||||
revidx = find_pack_revindex(p, base_offset);
|
||||
if (!revidx)
|
||||
return NULL;
|
||||
base_sha1 = nth_packed_object_sha1(p, revidx->nr);
|
||||
error("failed to read delta base object %s"
|
||||
" at offset %"PRIuMAX" from %s",
|
||||
sha1_to_hex(base_sha1), (uintmax_t)base_offset,
|
||||
@ -1706,6 +1731,8 @@ static void *unpack_delta_entry(struct packed_git *p,
|
||||
return result;
|
||||
}
|
||||
|
||||
int do_check_packed_object_crc;
|
||||
|
||||
void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
||||
enum object_type *type, unsigned long *sizep)
|
||||
{
|
||||
@ -1713,6 +1740,19 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
||||
off_t curpos = obj_offset;
|
||||
void *data;
|
||||
|
||||
if (do_check_packed_object_crc && p->index_version > 1) {
|
||||
struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
|
||||
unsigned long len = revidx[1].offset - obj_offset;
|
||||
if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
|
||||
const unsigned char *sha1 =
|
||||
nth_packed_object_sha1(p, revidx->nr);
|
||||
error("bad packed object CRC for %s",
|
||||
sha1_to_hex(sha1));
|
||||
mark_bad_packed_object(p, sha1);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*type = unpack_object_header(p, &w_curs, &curpos, sizep);
|
||||
switch (*type) {
|
||||
case OBJ_OFS_DELTA:
|
||||
@ -1966,7 +2006,14 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
|
||||
if (!find_pack_entry(sha1, &e, NULL))
|
||||
return status;
|
||||
}
|
||||
return packed_object_info(e.p, e.offset, sizep);
|
||||
|
||||
status = packed_object_info(e.p, e.offset, sizep);
|
||||
if (status < 0) {
|
||||
mark_bad_packed_object(e.p, sha1);
|
||||
status = sha1_object_info(sha1, sizep);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void *read_packed_sha1(const unsigned char *sha1,
|
||||
|
||||
Reference in New Issue
Block a user