From c3a6be23337192248d7bd694992030433fd17441 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Sep 2026 17:52:32 +0000 Subject: [PATCH] midx: validate incremental MIDX pack IDs Incremental MIDX support made object-offset pack IDs local to each layer and then converted them to chain-global IDs by adding `num_packs_in_base`. The conversion was introduced by 19419821bac5 (midx: teach `nth_midxed_pack_int_id()` about incremental MIDXs, 2024-08-06). Chain-aware pack preparation followed in 1820bd878c62 (midx: teach `prepare_midx_pack()` about incremental MIDXs, 2024-08-06), but the final `midx_fill_entry()` lookup remained tied to the original layer. Only with 8f909ff4e9e8 (packfile: recover when a multi-pack-index names a removed pack, 2026-08-29) did Coverity point out this issue: a local ID such as `UINT32_MAX` could wrap when the base-pack count was added, producing a plausible but incorrect global ID. After `prepare_midx_pack()` resolved the chain, `midx_fill_entry()` could then underflow or address the wrong layer while indexing the current layer's pack array, causing an invalid memory access and crashing Git. Validate each local pack ID against its layer's pack count before adding the base count, and obtain the final pack through `nth_midxed_pack()`, which resolves the correct MIDX layer. This prevents an invalid local ID from wrapping during conversion and ensures that the lookup uses the layer identified by the resolved chain-global ID. Assisted-by: GPT-5.6 Luna Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- midx.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/midx.c b/midx.c index 6d1c548e3d..6968fc1c00 100644 --- a/midx.c +++ b/midx.c @@ -583,10 +583,16 @@ off_t nth_midxed_offset(struct multi_pack_index *m, uint32_t pos) uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos) { - pos = midx_for_object(&m, pos); + uint32_t pack_int_id; - return m->num_packs_in_base + get_be32(m->chunk_object_offsets + - (off_t)pos * MIDX_CHUNK_OFFSET_WIDTH); + pos = midx_for_object(&m, pos); + pack_int_id = get_be32(m->chunk_object_offsets + + (off_t)pos * MIDX_CHUNK_OFFSET_WIDTH); + if (pack_int_id >= m->num_packs) + die(_("bad pack-int-id: %"PRIu32" (%"PRIu32" total packs)"), + pack_int_id, m->num_packs); + + return m->num_packs_in_base + pack_int_id; } enum midx_fill_result midx_fill_entry(struct multi_pack_index *m, @@ -606,7 +612,7 @@ enum midx_fill_result midx_fill_entry(struct multi_pack_index *m, if (prepare_midx_pack(m, pack_int_id)) return MIDX_FILL_OWNER_UNAVAILABLE; - p = m->packs[pack_int_id - m->num_packs_in_base]; + p = nth_midxed_pack(m, pack_int_id); /* * We are about to tell the caller where they can locate the