Merge branch 'pw/midx-repack-overflow-fix'
Integer overflow fix around code paths for "git multi-pack-index repack"..
* pw/midx-repack-overflow-fix:
midx docs: clarify tie breaking
midx: avoid negative array index
midx repack: avoid potential integer overflow on 64 bit systems
midx repack: avoid integer overflow on 32 bit systems
diff --git a/Documentation/git-multi-pack-index.adoc b/Documentation/git-multi-pack-index.adoc
index 631d5c7..b6cd0d7 100644
--- a/Documentation/git-multi-pack-index.adoc
+++ b/Documentation/git-multi-pack-index.adoc
@@ -38,10 +38,13 @@
+
--
--preferred-pack=<pack>::
- Optionally specify the tie-breaking pack used when
- multiple packs contain the same object. `<pack>` must
- contain at least one object. If not given, ties are
- broken in favor of the pack with the lowest mtime.
+ When specified, break ties in favor of this pack when
+ there are additional copies of its objects in other
+ packs. Ties for objects not found in the preferred
+ pack are always resolved in favor of the copy in the
+ pack with the highest mtime. If unspecified, the pack
+ with the lowest mtime is used by default. The
+ preferred pack must have at least one object.
--[no-]bitmap::
Control whether or not a multi-pack bitmap is written.
diff --git a/git-compat-util.h b/git-compat-util.h
index 36b9577..4678e21 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -668,6 +668,22 @@ static inline int cast_size_t_to_int(size_t a)
return (int)a;
}
+static inline uint64_t u64_mult(uint64_t a, uint64_t b)
+{
+ if (unsigned_mult_overflows(a, b))
+ die("uint64_t overflow: %"PRIuMAX" * %"PRIuMAX,
+ (uintmax_t)a, (uintmax_t)b);
+ return a * b;
+}
+
+static inline uint64_t u64_add(uint64_t a, uint64_t b)
+{
+ if (unsigned_add_overflows(a, b))
+ die("uint64_t overflow: %"PRIuMAX" + %"PRIuMAX,
+ (uintmax_t)a, (uintmax_t)b);
+ return a + b;
+}
+
/*
* Limit size of IO chunks, because huge chunks only cause pain. OS X
* 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
diff --git a/midx-write.c b/midx-write.c
index dd3b307..ba4a949 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -1566,7 +1566,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
_("Counting referenced objects"),
m->num_objects);
for (i = 0; i < m->num_objects; i++) {
- int pack_int_id = nth_midxed_pack_int_id(m, i);
+ uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
count[pack_int_id]++;
display_progress(progress, i + 1);
}
@@ -1697,21 +1697,31 @@ static void fill_included_packs_batch(struct repository *r,
total_size = 0;
for (i = 0; total_size < batch_size && i < m->num_packs; i++) {
- int pack_int_id = pack_info[i].pack_int_id;
+ uint32_t pack_int_id = pack_info[i].pack_int_id;
struct packed_git *p = m->packs[pack_int_id];
- size_t expected_size;
+ uint64_t expected_size;
if (!want_included_pack(r, m, pack_kept_objects, pack_int_id))
continue;
- expected_size = st_mult(p->pack_size,
- pack_info[i].referenced_objects);
+ /*
+ * Use shifted integer arithmetic to calculate the
+ * expected pack size to ~4 significant digits without
+ * overflow for packsizes less that 1PB.
+ */
+ expected_size = (uint64_t)pack_info[i].referenced_objects << 14;
expected_size /= p->num_objects;
+ expected_size = u64_mult(expected_size, p->pack_size);
+ expected_size = u64_add(expected_size, 1u << 13) >> 14;
if (expected_size >= batch_size)
continue;
- total_size += expected_size;
+ if (unsigned_add_overflows(total_size, (size_t)expected_size))
+ total_size = SIZE_MAX;
+ else
+ total_size += expected_size;
+
include_pack[pack_int_id] = 1;
}