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

Re: [PATCH] rebase --autosquash: fix a potential segfault

From
Johannes Schindelin <johannes.schindelin@gmx.de>
Date
May 8, 2020, 23:45 UTC
Message-ID
<nycvar.QRO.7.76.6.2005090112530.56@tvgsbejvaqbjf.bet>
In-Reply-To
<20200507191714.GA25306@coredump.intra.peff.net>
Hi Peff,
On Thu, 7 May 2020, Jeff King wrote:
Show 21 quoted lines
> On Wed, May 06, 2020 at 11:35:48PM +0200, Johannes Schindelin wrote:
>
> > > >> > +				next[i] = next[i2];
> > > >> > +				next[i2] = i;
> > > >> > +				continue;
> > > >> > +			}
> > > >>
> > > >> I do have one question, though. What happens if we add a second
> > > >> fixup-of-a-fixup?
> > > >
> > > > Thanks for asking this question, I was a little curious about it, too.
> > >
> > > Interesting that three people looked at the same patch and asked the
> > > same question in different ways ;-)
> >
> > Indeed!
> >
> > I am very grateful, as I had missed that, and it helped me figure out a
> > better way to do it, and v2 looks a lot nicer, too.
>
> OK, so your v2 addresses that. Does that mean it was broken in v1?
Yes.
> If so, then why didn't my test reveal it?
Let's disect this:
 i  hash oneline
#0  1234 foo
#1  5678 !fixup foo
#2  abcd !fixup 5678
#3  dbaf !fixup 5678
Let's follow the original code, i.e. before my v1:

When #1 is processed, i.e. when `i == 1`, it finds `i2 == 0` as target. So it sets `next[0]` as well as `tail[0]` to 1.

Then #2 is processed, i.e. `i == 2`, and it finds `i2 == 1` as target. It sets `next[1]` as well as `tail[1]` to 2.

Now #3 is processed, i.e. it also finds `i2 == 1` as target, so it looks at next[1], sees that it is already non-negative, so it sets `next[tail[1]]`, i.e. `next[2]` to 3. It also sets `tail[1]` to 3, but nobody cares about that because there is no further todo command.

Now, let's follow the code with my v1:

It actually does the same as before! Why, you ask? Because at no stage is there any non-negative `next[j]` whose corresponding `tail[j]` is negative. (Except after #3 was processed, at that stage, `next[2]` is non-negative but `tail[2]` still is negative, but as I said, noone cares because there are no remaining todo commands.)

So the crucial part to trigger this bug is to have a regular `fixup! <oneline>` _between_ the `fixup! <oneline>` and the `fixup! <hash>` targeting the latter. So I think I can modify your example accordingly:

	1234 foo
	5678 fixup! foo
	90ab fixup! foo
	abcd fixup! 5678
	dbaf fixup! 5678
Or using your actual shell commands:
  git commit -m base --allow-empty
  git commit --squash HEAD -m 'this is the first squash' --allow-empty
  s=$(git rev-parse HEAD)
  git commit --fixup HEAD^ --allow-empty # This is the crucial command
  git commit -m "squash! $s" -m 'this is the second squash' --allow-empty
  git commit -m "squash! $s" -m 'this is the third squash' --allow-empty
  git rebase -ki --autosquash --root

Note the cricual command `git commit --fixup HEAD^`. When processing that, `i == 2` and `i2 == 0` (as for `i == 1`), and before v1, this would have set `next[1]` but `tail[0]`! With v1, this would have led to #4 and #5 being exchanged. With v2, the role of `tail` would have been extended to not only talk about the beginning of a fixup/squash chain, but about _any_ target of a fixup/squash, even if it is in the middle of a chain.

So why does this work? Why does it still do the right thing _even after_ inserting a fixup in the middle of a chain?

That's the beauty: if I insert anything in the middle of it, the `tail` of the actual beginning of the fixup/squash chain won't need to be changed. It still points to the end of that chain.

All I need to ensure is that item `i` is not just appended to the "chain" starting at `i2`, but that it is _inserted_ at the end of that chain in case that it is actually part of a larger chain, i.e. that its `next[i]` is set correctly before making it the immediate successor of the target commit. Since all of the elements in `next` and `tail` are initialized to `-1` (i.e. "no next fixup/squash item after this"), it will even do the right thing when it should actually append: it will set `next[i]` to `-1` in that case.

> I'm not really doubting that your v2 works so much as trying to
> un-confuse myself about the whole situation (which in turn might lead to
> a more intelligent review).

I wish I was quicker in my responses because I think that this is really helpful a conversation. By "forcing my hand" on a thorough explanation, you really help me get clarity for myself about the actual underlying issues. So even if I still think that v2 is correct after writing up the above explanation, the degree of my confidence increased substantially.

Thanks, Dscho

Previous: Jeff KingNext: Johannes Schindelin via GitGitGadget
Message 8 of 23 in “rebase --autosquash: fix a potential segfault”
  1. rebase --autosquash: fix a potential segfaultJohannes Schindelin via GitGitGadget, May 4, 2020
  2. Junio C HamanoMay 4, 2020
  3. Jeff KingMay 4, 2020
  4. Taylor BlauMay 4, 2020
  5. Junio C HamanoMay 5, 2020
  6. Johannes SchindelinMay 6, 2020
  7. Jeff KingMay 7, 2020
  8. Johannes SchindelinMay 8, 2020
  9. rebase --autosquash: fix a potential segfaultJohannes Schindelin via GitGitGadget, May 5, 2020
  10. rebase --autosquash: fix a potential segfaultJohannes Schindelin via GitGitGadget, May 9, 2020
  11. Andrei RybakMay 6, 2020
  12. Johannes SchindelinMay 7, 2020
  13. Philip OakleyMay 8, 2020
  14. Andrei RybakMay 8, 2020
  15. Philip OakleyMay 8, 2020
  16. Philip OakleyMay 18, 2020
  17. Johannes SchindelinMay 18, 2020
  18. Philip OakleyMay 25, 2020
  19. 0/2 Clarify some of the fixup! documenationPhilip Oakley, May 25, 2020
  20. 2/2 doc: fixup/squash: remove ellipsis marks, use <line> for clarifyPhilip Oakley, May 25, 2020
  21. 1/2 doc: fixup/squash: clarify use of <oid-hash> in subject linePhilip Oakley, May 25, 2020
  22. Junio C HamanoMay 27, 2020
  23. Philip OakleyMay 29, 2020

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.