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:
@ -286,6 +286,7 @@ static unsigned long write_object(struct sha1file *f,
|
||||
*/
|
||||
|
||||
if (!to_reuse) {
|
||||
no_reuse:
|
||||
if (!usable_delta) {
|
||||
buf = read_sha1_file(entry->idx.sha1, &type, &size);
|
||||
if (!buf)
|
||||
@ -367,46 +368,60 @@ static unsigned long write_object(struct sha1file *f,
|
||||
struct revindex_entry *revidx;
|
||||
off_t offset;
|
||||
|
||||
if (entry->delta) {
|
||||
if (entry->delta)
|
||||
type = (allow_ofs_delta && entry->delta->idx.offset) ?
|
||||
OBJ_OFS_DELTA : OBJ_REF_DELTA;
|
||||
reused_delta++;
|
||||
}
|
||||
hdrlen = encode_header(type, entry->size, header);
|
||||
|
||||
offset = entry->in_pack_offset;
|
||||
revidx = find_pack_revindex(p, offset);
|
||||
datalen = revidx[1].offset - offset;
|
||||
if (!pack_to_stdout && p->index_version > 1 &&
|
||||
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
|
||||
die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
|
||||
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
|
||||
error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
|
||||
unuse_pack(&w_curs);
|
||||
goto no_reuse;
|
||||
}
|
||||
|
||||
offset += entry->in_pack_header_size;
|
||||
datalen -= entry->in_pack_header_size;
|
||||
if (!pack_to_stdout && p->index_version == 1 &&
|
||||
check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
|
||||
error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
|
||||
unuse_pack(&w_curs);
|
||||
goto no_reuse;
|
||||
}
|
||||
|
||||
if (type == OBJ_OFS_DELTA) {
|
||||
off_t ofs = entry->idx.offset - entry->delta->idx.offset;
|
||||
unsigned pos = sizeof(dheader) - 1;
|
||||
dheader[pos] = ofs & 127;
|
||||
while (ofs >>= 7)
|
||||
dheader[--pos] = 128 | (--ofs & 127);
|
||||
if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit)
|
||||
if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
|
||||
unuse_pack(&w_curs);
|
||||
return 0;
|
||||
}
|
||||
sha1write(f, header, hdrlen);
|
||||
sha1write(f, dheader + pos, sizeof(dheader) - pos);
|
||||
hdrlen += sizeof(dheader) - pos;
|
||||
reused_delta++;
|
||||
} else if (type == OBJ_REF_DELTA) {
|
||||
if (limit && hdrlen + 20 + datalen + 20 >= limit)
|
||||
if (limit && hdrlen + 20 + datalen + 20 >= limit) {
|
||||
unuse_pack(&w_curs);
|
||||
return 0;
|
||||
}
|
||||
sha1write(f, header, hdrlen);
|
||||
sha1write(f, entry->delta->idx.sha1, 20);
|
||||
hdrlen += 20;
|
||||
reused_delta++;
|
||||
} else {
|
||||
if (limit && hdrlen + datalen + 20 >= limit)
|
||||
if (limit && hdrlen + datalen + 20 >= limit) {
|
||||
unuse_pack(&w_curs);
|
||||
return 0;
|
||||
}
|
||||
sha1write(f, header, hdrlen);
|
||||
}
|
||||
|
||||
if (!pack_to_stdout && p->index_version == 1 &&
|
||||
check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
|
||||
die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
|
||||
copy_pack_data(f, p, &w_curs, offset, datalen);
|
||||
unuse_pack(&w_curs);
|
||||
reused++;
|
||||
@ -1016,9 +1031,11 @@ static void check_object(struct object_entry *entry)
|
||||
* We want in_pack_type even if we do not reuse delta
|
||||
* since non-delta representations could still be reused.
|
||||
*/
|
||||
used = unpack_object_header_gently(buf, avail,
|
||||
used = unpack_object_header_buffer(buf, avail,
|
||||
&entry->in_pack_type,
|
||||
&entry->size);
|
||||
if (used == 0)
|
||||
goto give_up;
|
||||
|
||||
/*
|
||||
* Determine if this is a delta and if so whether we can
|
||||
@ -1030,6 +1047,8 @@ static void check_object(struct object_entry *entry)
|
||||
/* Not a delta hence we've already got all we need. */
|
||||
entry->type = entry->in_pack_type;
|
||||
entry->in_pack_header_size = used;
|
||||
if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
|
||||
goto give_up;
|
||||
unuse_pack(&w_curs);
|
||||
return;
|
||||
case OBJ_REF_DELTA:
|
||||
@ -1046,19 +1065,25 @@ static void check_object(struct object_entry *entry)
|
||||
ofs = c & 127;
|
||||
while (c & 128) {
|
||||
ofs += 1;
|
||||
if (!ofs || MSB(ofs, 7))
|
||||
die("delta base offset overflow in pack for %s",
|
||||
sha1_to_hex(entry->idx.sha1));
|
||||
if (!ofs || MSB(ofs, 7)) {
|
||||
error("delta base offset overflow in pack for %s",
|
||||
sha1_to_hex(entry->idx.sha1));
|
||||
goto give_up;
|
||||
}
|
||||
c = buf[used_0++];
|
||||
ofs = (ofs << 7) + (c & 127);
|
||||
}
|
||||
if (ofs >= entry->in_pack_offset)
|
||||
die("delta base offset out of bound for %s",
|
||||
sha1_to_hex(entry->idx.sha1));
|
||||
ofs = entry->in_pack_offset - ofs;
|
||||
if (ofs <= 0 || ofs >= entry->in_pack_offset) {
|
||||
error("delta base offset out of bound for %s",
|
||||
sha1_to_hex(entry->idx.sha1));
|
||||
goto give_up;
|
||||
}
|
||||
if (reuse_delta && !entry->preferred_base) {
|
||||
struct revindex_entry *revidx;
|
||||
revidx = find_pack_revindex(p, ofs);
|
||||
if (!revidx)
|
||||
goto give_up;
|
||||
base_ref = nth_packed_object_sha1(p, revidx->nr);
|
||||
}
|
||||
entry->in_pack_header_size = used + used_0;
|
||||
@ -1078,6 +1103,7 @@ static void check_object(struct object_entry *entry)
|
||||
*/
|
||||
entry->type = entry->in_pack_type;
|
||||
entry->delta = base_entry;
|
||||
entry->delta_size = entry->size;
|
||||
entry->delta_sibling = base_entry->delta_child;
|
||||
base_entry->delta_child = entry;
|
||||
unuse_pack(&w_curs);
|
||||
@ -1092,6 +1118,8 @@ static void check_object(struct object_entry *entry)
|
||||
*/
|
||||
entry->size = get_size_from_delta(p, &w_curs,
|
||||
entry->in_pack_offset + entry->in_pack_header_size);
|
||||
if (entry->size == 0)
|
||||
goto give_up;
|
||||
unuse_pack(&w_curs);
|
||||
return;
|
||||
}
|
||||
@ -1101,6 +1129,7 @@ static void check_object(struct object_entry *entry)
|
||||
* with sha1_object_info() to find about the object type
|
||||
* at this point...
|
||||
*/
|
||||
give_up:
|
||||
unuse_pack(&w_curs);
|
||||
}
|
||||
|
||||
@ -1712,6 +1741,16 @@ static void prepare_pack(int window, int depth)
|
||||
|
||||
get_object_details();
|
||||
|
||||
/*
|
||||
* If we're locally repacking then we need to be doubly careful
|
||||
* from now on in order to make sure no stealth corruption gets
|
||||
* propagated to the new pack. Clients receiving streamed packs
|
||||
* should validate everything they get anyway so no need to incur
|
||||
* the additional cost here in that case.
|
||||
*/
|
||||
if (!pack_to_stdout)
|
||||
do_check_packed_object_crc = 1;
|
||||
|
||||
if (!nr_objects || !window || !depth)
|
||||
return;
|
||||
|
||||
|
Reference in New Issue
Block a user