Skip to content

Commit ef485e7

Browse files
authored
[2.51.0] midx-write: fix segfault and other cleanups (#787)
Users have reported crashes during background maintenance. Their memory dumps appear to have an access violation in close_pack(), but it doesn't make sense based on the current implementation. I finally figured out the problem after wading through a bunch of messiness in the midx-write.c file. This _definitely_ fixes the segfault, but it also does some of that cleanup. This is also going upstream in gitgitgadget#1965, but hopefully this version can be fast-tracked to microsoft/git for our users who need it quickly.
2 parents a042926 + abe22e5 commit ef485e7

File tree

2 files changed

+75
-60
lines changed

2 files changed

+75
-60
lines changed

midx-write.c

Lines changed: 58 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#define DISABLE_SIGN_COMPARE_WARNINGS
2-
31
#include "git-compat-util.h"
42
#include "abspath.h"
53
#include "config.h"
@@ -24,6 +22,7 @@
2422
#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
2523
#define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
2624
#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
25+
#define NO_PREFERRED_PACK (~((uint32_t)0))
2726

2827
extern int midx_checksum_valid(struct multi_pack_index *m);
2928
extern void clear_midx_files_ext(const char *object_dir, const char *ext,
@@ -104,7 +103,7 @@ struct write_midx_context {
104103
unsigned large_offsets_needed:1;
105104
uint32_t num_large_offsets;
106105

107-
int preferred_pack_idx;
106+
uint32_t preferred_pack_idx;
108107

109108
int incremental;
110109
uint32_t num_multi_pack_indexes_before;
@@ -260,7 +259,7 @@ static void midx_fanout_sort(struct midx_fanout *fanout)
260259
static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
261260
struct multi_pack_index *m,
262261
uint32_t cur_fanout,
263-
int preferred_pack)
262+
uint32_t preferred_pack)
264263
{
265264
uint32_t start = m->num_objects_in_base, end;
266265
uint32_t cur_object;
@@ -274,7 +273,7 @@ static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
274273
end = m->num_objects_in_base + ntohl(m->chunk_oid_fanout[cur_fanout]);
275274

276275
for (cur_object = start; cur_object < end; cur_object++) {
277-
if ((preferred_pack > -1) &&
276+
if ((preferred_pack != NO_PREFERRED_PACK) &&
278277
(preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
279278
/*
280279
* Objects from preferred packs are added
@@ -364,7 +363,8 @@ static void compute_sorted_entries(struct write_midx_context *ctx,
364363
preferred, cur_fanout);
365364
}
366365

367-
if (-1 < ctx->preferred_pack_idx && ctx->preferred_pack_idx < start_pack)
366+
if (ctx->preferred_pack_idx != NO_PREFERRED_PACK &&
367+
ctx->preferred_pack_idx < start_pack)
368368
midx_fanout_add_pack_fanout(&fanout, ctx->info,
369369
ctx->preferred_pack_idx, 1,
370370
cur_fanout);
@@ -843,7 +843,7 @@ static int write_midx_bitmap(struct write_midx_context *ctx,
843843
uint32_t commits_nr,
844844
unsigned flags)
845845
{
846-
int ret, i;
846+
int ret;
847847
uint16_t options = 0;
848848
struct bitmap_writer writer;
849849
struct pack_idx_entry **index;
@@ -871,7 +871,7 @@ static int write_midx_bitmap(struct write_midx_context *ctx,
871871
* this order).
872872
*/
873873
ALLOC_ARRAY(index, pdata->nr_objects);
874-
for (i = 0; i < pdata->nr_objects; i++)
874+
for (uint32_t i = 0; i < pdata->nr_objects; i++)
875875
index[i] = &pdata->objects[i].idx;
876876

877877
bitmap_writer_init(&writer, ctx->repo, pdata,
@@ -892,7 +892,7 @@ static int write_midx_bitmap(struct write_midx_context *ctx,
892892
* happens between bitmap_writer_build_type_index() and
893893
* bitmap_writer_finish().
894894
*/
895-
for (i = 0; i < pdata->nr_objects; i++)
895+
for (uint32_t i = 0; i < pdata->nr_objects; i++)
896896
index[ctx->pack_order[i]] = &pdata->objects[i].idx;
897897

898898
bitmap_writer_select_commits(&writer, commits, commits_nr);
@@ -920,39 +920,21 @@ static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
920920
return get_multi_pack_index(source);
921921
}
922922

923-
static int fill_packs_from_midx(struct write_midx_context *ctx,
924-
const char *preferred_pack_name, uint32_t flags)
923+
static int fill_packs_from_midx(struct write_midx_context *ctx)
925924
{
926925
struct multi_pack_index *m;
927926

928927
for (m = ctx->m; m; m = m->base_midx) {
929928
uint32_t i;
930929

931930
for (i = 0; i < m->num_packs; i++) {
932-
ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
933-
934-
/*
935-
* If generating a reverse index, need to have
936-
* packed_git's loaded to compare their
937-
* mtimes and object count.
938-
*
939-
* If a preferred pack is specified, need to
940-
* have packed_git's loaded to ensure the chosen
941-
* preferred pack has a non-zero object count.
942-
*/
943-
if (flags & MIDX_WRITE_REV_INDEX ||
944-
preferred_pack_name) {
945-
if (prepare_midx_pack(ctx->repo, m,
946-
m->num_packs_in_base + i)) {
947-
error(_("could not load pack"));
948-
return 1;
949-
}
950-
951-
if (open_pack_index(m->packs[i]))
952-
die(_("could not open index for %s"),
953-
m->packs[i]->pack_name);
931+
if (prepare_midx_pack(ctx->repo, m,
932+
m->num_packs_in_base + i)) {
933+
error(_("could not load pack"));
934+
return 1;
954935
}
955936

937+
ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
956938
fill_pack_info(&ctx->info[ctx->nr++], m->packs[i],
957939
m->pack_names[i],
958940
m->num_packs_in_base + i);
@@ -1056,11 +1038,13 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
10561038
{
10571039
struct strbuf midx_name = STRBUF_INIT;
10581040
unsigned char midx_hash[GIT_MAX_RAWSZ];
1059-
uint32_t i, start_pack;
1041+
uint32_t start_pack;
10601042
struct hashfile *f = NULL;
10611043
struct lock_file lk;
10621044
struct tempfile *incr;
1063-
struct write_midx_context ctx = { 0 };
1045+
struct write_midx_context ctx = {
1046+
.preferred_pack_idx = NO_PREFERRED_PACK,
1047+
};
10641048
int bitmapped_packs_concat_len = 0;
10651049
int pack_name_concat_len = 0;
10661050
int dropped_packs = 0;
@@ -1123,8 +1107,8 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
11231107
ctx.num_multi_pack_indexes_before++;
11241108
m = m->base_midx;
11251109
}
1126-
} else if (ctx.m && fill_packs_from_midx(&ctx, preferred_pack_name,
1127-
flags) < 0) {
1110+
} else if (ctx.m && fill_packs_from_midx(&ctx)) {
1111+
result = 1;
11281112
goto cleanup;
11291113
}
11301114

@@ -1168,22 +1152,22 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
11681152
goto cleanup; /* nothing to do */
11691153

11701154
if (preferred_pack_name) {
1171-
ctx.preferred_pack_idx = -1;
1155+
ctx.preferred_pack_idx = NO_PREFERRED_PACK;
11721156

1173-
for (i = 0; i < ctx.nr; i++) {
1157+
for (size_t i = 0; i < ctx.nr; i++) {
11741158
if (!cmp_idx_or_pack_name(preferred_pack_name,
11751159
ctx.info[i].pack_name)) {
11761160
ctx.preferred_pack_idx = i;
11771161
break;
11781162
}
11791163
}
11801164

1181-
if (ctx.preferred_pack_idx == -1)
1165+
if (ctx.preferred_pack_idx == NO_PREFERRED_PACK)
11821166
warning(_("unknown preferred pack: '%s'"),
11831167
preferred_pack_name);
11841168
} else if (ctx.nr &&
11851169
(flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
1186-
struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
1170+
struct packed_git *oldest = ctx.info[0].p;
11871171
ctx.preferred_pack_idx = 0;
11881172

11891173
if (packs_to_drop && packs_to_drop->nr)
@@ -1195,7 +1179,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
11951179
* pack-order has all of its objects selected from that pack
11961180
* (and not another pack containing a duplicate)
11971181
*/
1198-
for (i = 1; i < ctx.nr; i++) {
1182+
for (size_t i = 1; i < ctx.nr; i++) {
11991183
struct packed_git *p = ctx.info[i].p;
12001184

12011185
if (!oldest->num_objects || p->mtime < oldest->mtime) {
@@ -1211,18 +1195,23 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
12111195
* objects to resolve, so the preferred value doesn't
12121196
* matter.
12131197
*/
1214-
ctx.preferred_pack_idx = -1;
1198+
ctx.preferred_pack_idx = NO_PREFERRED_PACK;
12151199
}
12161200
} else {
12171201
/*
12181202
* otherwise don't mark any pack as preferred to avoid
12191203
* interfering with expiration logic below
12201204
*/
1221-
ctx.preferred_pack_idx = -1;
1205+
ctx.preferred_pack_idx = NO_PREFERRED_PACK;
12221206
}
12231207

1224-
if (ctx.preferred_pack_idx > -1) {
1208+
if (ctx.preferred_pack_idx != NO_PREFERRED_PACK) {
12251209
struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
1210+
1211+
if (open_pack_index(preferred))
1212+
die(_("failed to open preferred pack %s"),
1213+
ctx.info[ctx.preferred_pack_idx].pack_name);
1214+
12261215
if (!preferred->num_objects) {
12271216
error(_("cannot select preferred pack %s with no objects"),
12281217
preferred->pack_name);
@@ -1234,7 +1223,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
12341223
compute_sorted_entries(&ctx, start_pack);
12351224

12361225
ctx.large_offsets_needed = 0;
1237-
for (i = 0; i < ctx.entries_nr; i++) {
1226+
for (size_t i = 0; i < ctx.entries_nr; i++) {
12381227
if (ctx.entries[i].offset > 0x7fffffff)
12391228
ctx.num_large_offsets++;
12401229
if (ctx.entries[i].offset > 0xffffffff)
@@ -1244,10 +1233,10 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
12441233
QSORT(ctx.info, ctx.nr, pack_info_compare);
12451234

12461235
if (packs_to_drop && packs_to_drop->nr) {
1247-
int drop_index = 0;
1236+
size_t drop_index = 0;
12481237
int missing_drops = 0;
12491238

1250-
for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
1239+
for (size_t i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
12511240
int cmp = strcmp(ctx.info[i].pack_name,
12521241
packs_to_drop->items[drop_index].string);
12531242

@@ -1278,7 +1267,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
12781267
* pack_perm[old_id] = new_id
12791268
*/
12801269
ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
1281-
for (i = 0; i < ctx.nr; i++) {
1270+
for (size_t i = 0; i < ctx.nr; i++) {
12821271
if (ctx.info[i].expired) {
12831272
dropped_packs++;
12841273
ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
@@ -1287,7 +1276,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
12871276
}
12881277
}
12891278

1290-
for (i = 0; i < ctx.nr; i++) {
1279+
for (size_t i = 0; i < ctx.nr; i++) {
12911280
if (ctx.info[i].expired)
12921281
continue;
12931282
pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
@@ -1334,13 +1323,15 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
13341323
incr = mks_tempfile_m(midx_name.buf, 0444);
13351324
if (!incr) {
13361325
error(_("unable to create temporary MIDX layer"));
1337-
return -1;
1326+
result = -1;
1327+
goto cleanup;
13381328
}
13391329

13401330
if (adjust_shared_perm(r, get_tempfile_path(incr))) {
13411331
error(_("unable to adjust shared permissions for '%s'"),
13421332
get_tempfile_path(incr));
1343-
return -1;
1333+
result = -1;
1334+
goto cleanup;
13441335
}
13451336

13461337
f = hashfd(r->hash_algo, get_tempfile_fd(incr),
@@ -1431,6 +1422,9 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
14311422
* have been freed in the previous if block.
14321423
*/
14331424

1425+
if (ctx.num_multi_pack_indexes_before == UINT32_MAX)
1426+
die("too many multi-pack-indexes");
1427+
14341428
CALLOC_ARRAY(keep_hashes, ctx.num_multi_pack_indexes_before + 1);
14351429

14361430
if (ctx.incremental) {
@@ -1440,34 +1434,38 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
14401434

14411435
if (!chainf) {
14421436
error_errno(_("unable to open multi-pack-index chain file"));
1443-
return -1;
1437+
result = -1;
1438+
goto cleanup;
14441439
}
14451440

1446-
if (link_midx_to_chain(ctx.base_midx) < 0)
1447-
return -1;
1441+
if (link_midx_to_chain(ctx.base_midx) < 0) {
1442+
result = -1;
1443+
goto cleanup;
1444+
}
14481445

14491446
get_split_midx_filename_ext(r->hash_algo, &final_midx_name,
14501447
object_dir, midx_hash, MIDX_EXT_MIDX);
14511448

14521449
if (rename_tempfile(&incr, final_midx_name.buf) < 0) {
14531450
error_errno(_("unable to rename new multi-pack-index layer"));
1454-
return -1;
1451+
result = -1;
1452+
goto cleanup;
14551453
}
14561454

14571455
strbuf_release(&final_midx_name);
14581456

14591457
keep_hashes[ctx.num_multi_pack_indexes_before] =
14601458
xstrdup(hash_to_hex_algop(midx_hash, r->hash_algo));
14611459

1462-
for (i = 0; i < ctx.num_multi_pack_indexes_before; i++) {
1460+
for (uint32_t i = 0; i < ctx.num_multi_pack_indexes_before; i++) {
14631461
uint32_t j = ctx.num_multi_pack_indexes_before - i - 1;
14641462

14651463
keep_hashes[j] = xstrdup(hash_to_hex_algop(get_midx_checksum(m),
14661464
r->hash_algo));
14671465
m = m->base_midx;
14681466
}
14691467

1470-
for (i = 0; i < ctx.num_multi_pack_indexes_before + 1; i++)
1468+
for (uint32_t i = 0; i <= ctx.num_multi_pack_indexes_before; i++)
14711469
fprintf(get_lock_file_fp(&lk), "%s\n", keep_hashes[i]);
14721470
} else {
14731471
keep_hashes[ctx.num_multi_pack_indexes_before] =
@@ -1485,7 +1483,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
14851483
ctx.incremental);
14861484

14871485
cleanup:
1488-
for (i = 0; i < ctx.nr; i++) {
1486+
for (size_t i = 0; i < ctx.nr; i++) {
14891487
if (ctx.info[i].p) {
14901488
close_pack(ctx.info[i].p);
14911489
free(ctx.info[i].p);
@@ -1498,7 +1496,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
14981496
free(ctx.pack_perm);
14991497
free(ctx.pack_order);
15001498
if (keep_hashes) {
1501-
for (i = 0; i < ctx.num_multi_pack_indexes_before + 1; i++)
1499+
for (uint32_t i = 0; i <= ctx.num_multi_pack_indexes_before; i++)
15021500
free((char *)keep_hashes[i]);
15031501
free(keep_hashes);
15041502
}

t/t5319-multi-pack-index.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,23 @@ test_expect_success 'repack --batch-size=0 repacks everything' '
989989
)
990990
'
991991

992+
test_expect_success EXPENSIVE 'repack/expire with many packs' '
993+
cp -r dup many &&
994+
(
995+
cd many &&
996+
997+
for i in $(test_seq 1 100)
998+
do
999+
test_commit extra$i &&
1000+
git maintenance run --task=loose-objects || return 1
1001+
done &&
1002+
1003+
git multi-pack-index write &&
1004+
git multi-pack-index repack &&
1005+
git multi-pack-index expire
1006+
)
1007+
'
1008+
9921009
test_expect_success 'repack --batch-size=<large> repacks everything' '
9931010
(
9941011
cd dup2 &&

0 commit comments

Comments
 (0)