git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] remote: initialize values that might not be set

From
Johannes Schindelin <johannes.schindelin@gmx.de>
Date
Jun 10, 2021, 09:24 UTC
Message-ID
<nycvar.QRO.7.76.6.2106101046470.57@tvgsbejvaqbjf.bet>
In-Reply-To
<xmqqlf7ltg4q.fsf@gitster.g>
Hi Junio,
On Tue, 8 Jun 2021, Junio C Hamano wrote:
Show 25 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> >> diff --git a/remote.c b/remote.c
> >> index c3f85c17ca7c..a116392fb057 100644
> >> --- a/remote.c
> >> +++ b/remote.c
> >> @@ -2101,7 +2101,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
> >>  int format_tracking_info(struct branch *branch, struct strbuf *sb,
> >>  			 enum ahead_behind_flags abf)
> >>  {
> >> -	int ours, theirs, sti;
> >> +	int ours = 0, theirs = 0, sti = 0;
> >
> > While I like this change, I am somewhat confused where the values are used
> > for branching. The only time I see them used when `stat_branch_pair()` has
> > _not_ initialized `ours` and `theirs` is in those `trace2_data_intmax()`
> > calls. Otherwise `sti` is set to -1 and the other users of `ours` and
> > `theirs` aren't reached.
> >
> > If my reading of the code is correct, maybe the commit message could be
> > adjusted to talk about tracing instead of branching?
>
> I too wondered why initializing them to 0 is safe (instead of hiding
> latent bugs).  I think that stat_tracking_info() would always return
> -1 if returns before reaching the point in stat_branch_pair(),

While that is true, I was trying to make a different point: I noticed that the `ours`/`theirs` variables _are_ used, even if `sti` is negative. The code that I looked at reads like this:

	int format_tracking_info(struct branch *branch, struct strbuf *sb,
				 enum ahead_behind_flags abf)
	{
		int ours, theirs, sti;
		const char *full_base;
		char *base;
		int upstream_is_gone = 0;
		trace2_region_enter("tracking", "stat_tracking_info", NULL);
		sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
		trace2_data_intmax("tracking", NULL, "stat_tracking_info/ab_flags", abf);
		trace2_data_intmax("tracking", NULL, "stat_tracking_info/ab_result", sti);
		if (abf == AHEAD_BEHIND_FULL) {
		    trace2_data_intmax("tracking", NULL, "stat_tracking_info/ab_ahead", ours);
		    trace2_data_intmax("tracking", NULL, "stat_tracking_info/ab_behind", theirs);
		}
		trace2_region_leave("tracking", "stat_tracking_info", NULL);
		if (sti < 0) {
			if (!full_base)
				return 0;
			upstream_is_gone = 1;
		}

You will notice that there are two Trace2 calls in that conditional `abf == AHEAD_BEHIND_FULL` block.

Now, what I failed to realize when reviewing this code (and I _bet_ Stolee was in the same boat when they contributed the patch) is that this version of `format_tracking_info()` is different from what is in v2.32.0. It is the version we have in the `microsoft/git` fork, and it has not yet made it upstream. To be precise, it is this commit: https://github.com/microsoft/git/commit/91209e591b0398c8334a78001a245807f7eb348a

In light of this, it might make more sense for us to fixup! this commit thusly:

-- snip --
diff --git a/remote.c b/remote.c
index caed9cbc31b1..cfb7b6bd8d30 100644
--- a/remote.c
+++ b/remote.c
@@ -2110,7 +2110,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
 	sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
 	trace2_data_intmax("tracking", NULL, "stat_tracking_info/ab_flags", abf);
 	trace2_data_intmax("tracking", NULL, "stat_tracking_info/ab_result", sti);
-	if (abf == AHEAD_BEHIND_FULL) {
+	if (sti >= 0 && abf == AHEAD_BEHIND_FULL) {
 	    trace2_data_intmax("tracking", NULL, "stat_tracking_info/ab_ahead", ours);
 	    trace2_data_intmax("tracking", NULL, "stat_tracking_info/ab_behind", theirs);
 	}
-- snap --

This would be in line with how `format_tracking_info()` avoids accessing
`ours` and `theirs` if `stat_tracking_info()` returned a negative value.

I opened the corresponding PR here:
https://github.com/microsoft/git/pull/373

> but it is not clear how we can futureproof the whole thing.
>
> If these two are initialized to say -1 here, and then we had some
> sanity check, perhaps like so:
>
> 	sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
> +	assert(sti < 0 || (0 <= ours && 0 <= theirs));
> 	if (sti < 0) {
> 		if (!full_base)
> 	...
>
> to enforce the invariant we assume (i.e. OK sti means ours and
> theirs are set), it would allow us to sleep better, perhaps?

As I have stated elsewhere, I am somewhat doubtful of the benefit those
`assert()` calls give us.

I wish there was a way to integrate some sort of static analysis that
would warn us about using uninitialized values.

Of course, we would have to make sure that it does not show as many false
positives about `struct strbuf` and `struct strvec` "overrunning" on their
buffer. This is what dominates Coverity's report, for example.

FWIW I played a little with CodeQL on GitHub, but have not found time to
continue on that in a long time... my current state is pushed as `codeql`
to my fork: https://github.com/git/git/compare/master...dscho:codeql, just
in case somebody interested wants to take this further).

Ciao,
Dscho
Previous: Junio C HamanoNext: Junio C Hamano
Message 4 of 6 in “remote: initialize values that might not be set”
  1. remote: initialize values that might not be setDerrick Stolee via GitGitGadget, Jun 7, 2021
  2. Johannes SchindelinJun 7, 2021
  3. Junio C HamanoJun 7, 2021
  4. Johannes SchindelinJun 10, 2021
  5. Junio C HamanoJun 11, 2021
  6. Derrick StoleeJun 11, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.