From: "Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Johannes Schindelin <johannes.schindelin@gmx.de>
Subject: [PATCH v2 00/12] coverity: avoid dereferencing NULL
Date: Fri, 10 Jul 2026 11:39:24 +0000 [thread overview]
Message-ID: <pull.2174.v2.git.1783683577.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2174.git.1783590159.gitgitgadget@gmail.com>
This is a continuation of the effort I started in the patch series that
became js/coverity-fixes. This next batch adds guards to avoid dereferencing
NULL pointers and accessing NULL file descriptors.
Changes since v1:
* Calling remote_tracking() no longer returns -1 when remote is NULL, but
instead BUG()s out.
* bisect_successful() returns with BISECT_FAILED instead of the -1 that
only worked by happenstance.
* The commit "revision: avoid dereferencing NULL in add_parents_only()" now
comes with a regression test.
* The commit "bisect: ensure non-NULL head before using it" no longer
claims that the fixed bug can be triggered with the current code base.
* The missing shallow commit's OID is no longer computed twice.
* A follow-up commit was folded into this patch series that lets
write_one_shallow() avoid the rolling buffers of oid_to_hex(), as
suggested by Junio. It technically does not fit the goal of this patch
series (fixing issues pointed out by Coverity), but was asked for
explicitly.
Johannes Schindelin (12):
diffcore-break: guard against NULLed queue entries in merge loop
diff: handle NULL return from repo_get_commit_tree()
remote: guard `remote_tracking()` against NULL remote
reftable/stack: guard against NULL list_file in stack_destroy
mailsplit: move NULL check before first use of file handle
bisect: handle NULL commit in `bisect_successful()`
replay: die when --onto does not peel to a commit
revision: avoid dereferencing NULL in `add_parents_only()`
pack-bitmap: handle missing bitmap for base MIDX
bisect: ensure non-NULL `head` before using it
shallow: fix NULL dereference
shallow: give write_one_shallow() its own hex buffer
builtin/bisect.c | 9 ++++++++-
builtin/diff.c | 10 +++++++---
builtin/mailsplit.c | 6 +++---
diffcore-break.c | 2 ++
pack-bitmap.c | 4 ++++
reftable/stack.c | 3 ++-
remote.c | 2 ++
replay.c | 8 ++++++--
revision.c | 9 +++++++--
shallow.c | 7 ++++---
t/t0410-partial-clone.sh | 18 ++++++++++++++++++
11 files changed, 63 insertions(+), 15 deletions(-)
base-commit: e9019fcafe0040228b8631c30f97ae1adb61bcdc
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2174%2Fdscho%2Fcoverity-fixes-null-safety-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2174/dscho/coverity-fixes-null-safety-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/2174
Range-diff vs v1:
1: df00334f8b = 1: df00334f8b diffcore-break: guard against NULLed queue entries in merge loop
2: 4fdba0542b = 2: 4fdba0542b diff: handle NULL return from repo_get_commit_tree()
3: dcaefc5987 ! 3: 1398a2f120 remote: guard `remote_tracking()` against NULL remote
@@ remote.c: static int remote_tracking(struct remote *remote, const char *refname,
char *dst;
+ if (!remote)
-+ return -1; /* no remote to look up tracking ref */
++ BUG("remote_tracking() called with NULL remote");
dst = apply_refspecs(&remote->fetch, refname);
if (!dst)
return -1; /* no tracking ref for refname at remote */
4: d7bc7fce35 = 4: 285f019fb3 reftable/stack: guard against NULL list_file in stack_destroy
5: 41eef047ae = 5: 12c2c8450e mailsplit: move NULL check before first use of file handle
6: 7041375108 ! 6: ca818ee405 bisect: handle NULL commit in `bisect_successful()`
@@ builtin/bisect.c: static int bisect_successful(struct bisect_terms *terms)
refs_read_ref(get_main_ref_store(the_repository), bad_ref, &oid);
commit = lookup_commit_reference_by_name(bad_ref);
+ if (!commit) {
-+ res = error(_("could not find commit for '%s'"), bad_ref);
++ error(_("could not find commit for '%s'"), bad_ref);
+ free(bad_ref);
-+ return res;
++ return BISECT_FAILED;
+ }
repo_format_commit_message(the_repository, commit, "%s", &commit_name,
&pp);
7: a7245cdffa = 7: 8216769be9 replay: die when --onto does not peel to a commit
8: 0675767797 ! 8: 41285dd8e1 revision: avoid dereferencing NULL in `add_parents_only()`
@@ revision.c: static int add_parents_only(struct rev_info *revs, const char *arg_,
if (it->type != OBJ_TAG)
break;
if (!((struct tag*)it)->tagged)
+
+ ## t/t0410-partial-clone.sh ##
+@@ t/t0410-partial-clone.sh: test_expect_success 'rev-list dies for missing objects on cmd line' '
+ done
+ '
+
++test_expect_success '--exclude-promisor-objects with ^@ on missing object' '
++ rm -rf repo &&
++ test_create_repo repo &&
++ test_commit -C repo foo &&
++ test_commit -C repo bar &&
++
++ COMMIT=$(git -C repo rev-parse foo) &&
++ promise_and_delete "$COMMIT" &&
++
++ git -C repo config core.repositoryformatversion 1 &&
++ git -C repo config extensions.partialclone "arbitrary string" &&
++
++ # Ensure that "$COMMIT^@" is handled gracefully even though the
++ # actual commits are missing.
++ git -C repo rev-list --exclude-promisor-objects "$COMMIT^@" >out &&
++ test_must_be_empty out
++'
++
+ test_expect_success 'single promisor remote can be re-initialized gracefully' '
+ # ensure one promisor is in the promisors list
+ rm -rf repo &&
9: 0b27860478 = 9: cccd36137f pack-bitmap: handle missing bitmap for base MIDX
10: 428a3a006b ! 10: 376a6581cb bisect: ensure non-NULL `head` before using it
@@ Commit message
Later, that variable is passed to `repo_get_oid()` and `starts_with()`,
both of which would dereference the NULL pointer.
- The scenario "`refs_resolve_ref_unsafe()` returns NULL but
- `repo_get_oid()` succeeds" can happen when HEAD is a detached bare OID
- that the ref backend cannot resolve symbolically (a potential edge case
- with the reftable backend) but the OID itself is valid. In this case,
- the bisect-start file does not yet exist (this is a fresh "git bisect
- start"), so the else branch is taken with the NULL `head`.
-
- Simply assign "HEAD" to `head` as a fallback to address this.
-
- Pointed out by Coverity.
-
- Assisted-by: Claude Opus 4.6
+ A concrete trigger for `refs_resolve_ref_unsafe()` returning NULL while
+ `repo_get_oid()` succeeds could not be constructed against the ref
+ backends currently in the tree; the naive case (a symbolic HEAD pointing
+ at a nonexistent branch, in either the files or the reftable backend)
+ fails in both calls consistently and returns via the existing
+ `error(_("bad HEAD - I need a HEAD"))` path. Coverity, however, flags
+ the leftover use of `head` after the outer `if (!head)` on a formal
+ reading: `head` is still NULL at that point, and both `starts_with(head,
+ ...)` and the second `repo_get_oid(..., head, ...)` in the else-branch
+ would dereference it if that state were ever reached.
+
+ Removing the outer check would risk regressing to a crash if a future
+ ref backend ever manages to hit the "returns NULL for HEAD but has a
+ valid OID for HEAD" state. Assigning the literal string "HEAD" as a
+ safe fallback documents the intent and satisfies the analyzer without
+ changing behavior in any code path we can currently reach.
+
+ Assisted-by: Claude Opus 4.7
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
## builtin/bisect.c ##
11: 9f3a239484 ! 11: e581bc91ee shallow: fix NULL dereference
@@ Commit message
## shallow.c ##
@@ shallow.c: static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
+ struct commit *c = lookup_commit(the_repository, &graft->oid);
if (!c || !(c->object.flags & SEEN)) {
if (data->flags & VERBOSE)
- printf("Removing %s from .git/shallow\n",
+- printf("Removing %s from .git/shallow\n",
- oid_to_hex(&c->object.oid));
-+ oid_to_hex(&graft->oid));
++ printf("Removing %s from .git/shallow\n", hex);
return 0;
}
}
-: ---------- > 12: 2ef74b52fa shallow: give write_one_shallow() its own hex buffer
--
gitgitgadget
next prev parent reply other threads:[~2026-07-10 11:39 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 9:42 [PATCH 00/11] coverity: avoid dereferencing NULL Johannes Schindelin via GitGitGadget
2026-07-09 9:42 ` [PATCH 01/11] diffcore-break: guard against NULLed queue entries in merge loop Johannes Schindelin via GitGitGadget
2026-07-10 3:11 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 02/11] diff: handle NULL return from repo_get_commit_tree() Johannes Schindelin via GitGitGadget
2026-07-10 3:11 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 03/11] remote: guard `remote_tracking()` against NULL remote Johannes Schindelin via GitGitGadget
2026-07-10 3:21 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 04/11] reftable/stack: guard against NULL list_file in stack_destroy Johannes Schindelin via GitGitGadget
2026-07-10 3:21 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 05/11] mailsplit: move NULL check before first use of file handle Johannes Schindelin via GitGitGadget
2026-07-10 3:31 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 06/11] bisect: handle NULL commit in `bisect_successful()` Johannes Schindelin via GitGitGadget
2026-07-10 3:31 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 07/11] replay: die when --onto does not peel to a commit Johannes Schindelin via GitGitGadget
2026-07-09 9:42 ` [PATCH 08/11] revision: avoid dereferencing NULL in `add_parents_only()` Johannes Schindelin via GitGitGadget
2026-07-10 3:41 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 09/11] pack-bitmap: handle missing bitmap for base MIDX Johannes Schindelin via GitGitGadget
2026-07-10 3:41 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 10/11] bisect: ensure non-NULL `head` before using it Johannes Schindelin via GitGitGadget
2026-07-10 4:01 ` Junio C Hamano
2026-07-09 9:42 ` [PATCH 11/11] shallow: fix NULL dereference Johannes Schindelin via GitGitGadget
2026-07-09 20:10 ` Junio C Hamano
2026-07-10 11:39 ` Johannes Schindelin via GitGitGadget [this message]
2026-07-10 11:39 ` [PATCH v2 01/12] diffcore-break: guard against NULLed queue entries in merge loop Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 02/12] diff: handle NULL return from repo_get_commit_tree() Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 03/12] remote: guard `remote_tracking()` against NULL remote Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 04/12] reftable/stack: guard against NULL list_file in stack_destroy Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 05/12] mailsplit: move NULL check before first use of file handle Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 06/12] bisect: handle NULL commit in `bisect_successful()` Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 07/12] replay: die when --onto does not peel to a commit Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 08/12] revision: avoid dereferencing NULL in `add_parents_only()` Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 09/12] pack-bitmap: handle missing bitmap for base MIDX Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 10/12] bisect: ensure non-NULL `head` before using it Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 11/12] shallow: fix NULL dereference Johannes Schindelin via GitGitGadget
2026-07-10 11:39 ` [PATCH v2 12/12] shallow: give write_one_shallow() its own hex buffer Johannes Schindelin via GitGitGadget
2026-07-10 15:46 ` [PATCH v2 00/12] coverity: avoid dereferencing NULL Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.2174.v2.git.1783683577.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=johannes.schindelin@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox