Git development
 help / color / mirror / Atom feed
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

  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