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 19419821ba
(midx: teach `nth_midxed_pack_int_id()` about incremental MIDXs,
2024-08-06). Chain-aware pack preparation followed in 1820bd878c
(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 8f909ff4e9 (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 <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
jch
Johannes Schindelin 2026-09-17 17:52:32 +00:00 committed by Junio C Hamano
parent 723dd6ca3e
commit c3a6be2333
1 changed files with 10 additions and 4 deletions

14
midx.c
View File

@ -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