Skip to content

Fix issues pointed out in Git for Windows by Coverity after merging v2.56.0-rc0 - #2231

Open
dscho wants to merge 7 commits into
gitgitgadget:masterfrom
dscho:fix-coverity-high-severity
Open

dscho wants to merge 7 commits into
gitgitgadget:masterfrom
dscho:fix-coverity-high-severity

Conversation

@dscho

@dscho dscho commented Sep 17, 2026

Copy link
Copy Markdown
Member

These Coverity reports are new as of this -rc cycle; Apart from the writev one, I don't think any of these are pressing, in most cases I am still puzzled why they were reported only now.

@gitgitgadget

gitgitgadget Bot commented Sep 17, 2026

Copy link
Copy Markdown

There is an issue in commit 957966d:
test-read-midx: check midx_fill_entry() result

  • Lines in the body of the commit messages should be wrapped between 60 and 76 characters.
    Indented lines, and lines without whitespace, are exempt

As Git for Windows' Coverity run after merging v2.56.0-rc0 reported,
`writev_in_full()` keeps its cumulative successful output in an
`ssize_t`. Although `xwritev()` limits each individual write to a
syscall-sized amount, repeated successful writes can still exceed
`SSIZE_MAX`. The unchecked accumulation was introduced by d70eb7f
(wrapper: introduce writev(3p) wrappers, 2026-08-07).

Treat an aggregate that would overflow the signed total as an I/O
failure.

Assisted-by: GPT-5.6 Luna
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
After merging v2.56.0-rc0 into Git for Windows, its Coverity run
reported the following issue: The `parse_signed_buffer()` function
accepts object buffers with an explicit size, while
`get_format_by_sig()` uses `starts_with()`, i.e. it expects a
NUL-terminated buffer. A tag object with a non-NUL-terminated payload
ending in a partial signature prefix, such as a final '-' byte, could
therefore cause an invalid read past the object buffer.

The observable consequences are limited to reading past the allocation.
In practice it can crash Git if the read enters an unmapped page. It can
also misplace the payload/signature split, corrupting the compat-hash
object being written.

The older unbounded matcher predates this path, but c8762c3
(object-file-convert: convert tag objects when writing, 2023-10-01)
exposed the defect by passing exact-sized converted tag buffers to
`parse_signed_buffer()`. That commit first shipped in v2.45.0, so the
defect has been latent in every release since.

This pattern was noticed on the mailing list in February 2024. Reviewing
a patch for a very similar issue in commit.c's find_header_mem(), Jeff
King observed in
https://lore.kernel.org/git/20240208214137.GB1090198@coredump.intra.peff.net/:

  But more interestingly: even though we pass a buf/len pair to
  parse_signed_buffer(), it then calls get_format_by_sig() which takes
  only a NUL-terminated string. [...] That raises the question of
  whether parse_signed_buffer() has a similar walk-too-far problem. ;)
  The answer is no, because we feed it from a strbuf. But it's not a
  great pattern overall.

That reasoning surveyed the callers that existed at the time and missed
c8762c3 (object-file-convert: convert tag objects when writing,
2023-10-01), which was four months old at that time, and does not feed
from a strbuf; `convert_tag_object()` hands `parse_signed_buffer()` an
exact-sized `xmalloc()` buffer, and the concern flagged and dismissed in
that thread is exactly the defect Coverity now reports.

Jeff went on to add `starts_with_mem()` a month later, in
https://lore.kernel.org/git/20240307092638.GK2080210@coredump.intra.peff.net/,
precisely for "cases where the buffer is not NUL-terminated (and we
instead have an explicit size or end pointer)", so the tool for this fix
has been in the tree since v2.45.0.

Even though the issue had been latent, it most likely surfaced via
Coverity because of 215d305 (odb: compute compat object ID in
`odb_write_object_ext()`, 2026-07-17), which moved
`convert_object_file()` out of the `source->write_object` function
pointer into a direct call in `odb_write_object_ext()`.

Preserve the existing NUL-terminated behavior for callers that provide
strings while making signature-prefix matching honor the known buffer
lengths, via the `starts_with_mem()` helper. This keeps reads within the
object data without implying exploitability beyond the observed invalid
read.

Assisted-by: GPT-5.6 Luna
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
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 1941982
(midx: teach `nth_midxed_pack_int_id()` about incremental MIDXs,
2024-08-06). Chain-aware pack preparation followed in 1820bd8
(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 8f909ff (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>
`rerere` can mark a conflict variant as resolved even when writing its
preimage or postimage fails. A later invocation may then replay
incomplete data from the cache, turning a local filesystem failure into
an incorrect working-tree change.

629716d (rerere: do use multiple variants, 2015-07-30) introduced
the code paths without checks for those I/O results. Treat such failures
as failures, report them, and leave the rerere status unchanged unless
the corresponding data was recorded successfully.

The defect has been latent since 2015. Git for Windows' Coverity run
only reported it after merging v2.56.0-rc0, for reasons that could not
be figured out in a reasonable amount of time.

Assisted-by: GPT-5.6 Luna
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Coverity pointed out that the
`test_reftable_table__seek_invalid_log_offset()` test, which was
introduced by a1c085d (reftable/table: fix NULL pointer access when
seeking to bogus offsets, 2026-07-03), ignores the result of
`reftable_table_init_log_iterator()` and proceeds to
`reftable_iterator_seek_log()`, although initialization can return
`REFTABLE_OUT_OF_MEMORY_ERROR` without installing an ops table. Under
allocation failure, the test then dereferences a NULL function table.

Assert successful iterator initialization before seeking.

Assisted-by: GPT-5.6 Luna
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The reftable fuzzer introduced by adf4516 (oss-fuzz: add fuzzer for
parsing reftables, 2026-07-03) ignored failures from
`reftable_table_init_ref_iterator()` and
`reftable_table_init_log_iterator()`. Coverity reported that under
allocation failure, either constructor can return
`REFTABLE_OUT_OF_MEMORY_ERROR` without installing an ops table, allowing
a subsequent seek to dereference NULL.

Treat iterator initialization failure as a reason to skip the
corresponding seek and iteration while retaining safe destruction for an
uninitialized iterator.

Assisted-by: GPT-5.6 Luna
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The `--show-objects` mode of `read_midx_file()` uses the output of
`midx_fill_entry()` without checking whether the lookup succeeded. A
failed lookup or unavailable pack can leave that output unusable,
allowing malformed or concurrently changed MIDX data to make this test
helper crash instead of reporting a controlled error.

Reject the entry unless `midx_fill_entry()` returns `MIDX_FILL_HIT`. The
unchecked call was introduced by 86d174b
(t/helper/test-read-midx.c: add '--show-objects', 2021-03-30); later
incremental-MIDX changes expanded the possible failure modes, but this
remains a test-helper robustness issue, not a production Git attack
surface or an arbitrary-code-execution vulnerability.

It is unclear why Coverity reports this issue in Git for Windows only
after merging v2.56.0-rc0; The issue was not reported before.

Assisted-by: GPT-5.6 Luna
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho
dscho force-pushed the fix-coverity-high-severity branch from 957966d to 60599d2 Compare September 17, 2026 17:48
@dscho

dscho commented Sep 17, 2026

Copy link
Copy Markdown
Member Author

/submit

@gitgitgadget

gitgitgadget Bot commented Sep 17, 2026

Copy link
Copy Markdown

Submitted as pull.2231.git.1789667556.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-2231/dscho/fix-coverity-high-severity-v1

To fetch this version to local tag pr-2231/dscho/fix-coverity-high-severity-v1:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-2231/dscho/fix-coverity-high-severity-v1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant