Subject: Re: [PATCH v3] reflog-walk: don't segfault on non-commit sha1's in the reflog

From: Dennis Kaarsemaker <[email-masked]>
Date: Thu, 31 Dec 2015 09:57:07 +0100

On wo, 2015-12-30 at 16:02 -0800, Junio C Hamano wrote:
> Dennis Kaarsemaker <[email-masked]> writes:
> 
> > diff --git a/reflog-walk.c b/reflog-walk.c
> > index 85b8a54..0ebd1da 100644
> > --- a/reflog-walk.c
> > +++ b/reflog-walk.c
> > @@ -221,6 +221,7 @@ void fake_reflog_parent(struct reflog_walk_info
> > *info, struct commit *commit)
> >  	struct commit_info *commit_info =
> >  		get_commit_info(commit, &info->reflogs, 0);
> >  	struct commit_reflog *commit_reflog;
> > +	struct object *logobj;
> 
> This thing is not initialized...
> 
> >  	struct reflog_info *reflog;
> >  
> >  	info->last_commit_reflog = NULL;
> > @@ -232,15 +233,20 @@ void fake_reflog_parent(struct
> > reflog_walk_info *info, struct commit *commit)
> >  		commit->parents = NULL;
> >  		return;
> >  	}
> > -
> > -	reflog = &commit_reflog->reflogs->items[commit_reflog
> > ->recno];
> >  	info->last_commit_reflog = commit_reflog;
> > -	commit_reflog->recno--;
> > -	commit_info->commit = (struct commit *)parse_object(reflog
> > ->osha1);
> > -	if (!commit_info->commit) {
> > +
> > +	do {
> > +		reflog = &commit_reflog->reflogs
> > ->items[commit_reflog->recno];
> > +		commit_reflog->recno--;
> > +		logobj = parse_object(reflog->osha1);
> > +	} while (commit_reflog->recno && (logobj && logobj->type
> > != OBJ_COMMIT));
> 
> But this loop runs at least once, so logobj will always have some
> sane value when the loop exits.
> 
> > +	if (!logobj || logobj->type != OBJ_COMMIT) {
> 
> And the only case where this should trigger is when we ran out of
> recno.  Am I reading the updated code correctly?

Yes, your description matches what I tried to implement.

> With the updated code, the number of times we return from this
> function is different from the number initially set to recno.  I had
> to wonder if the caller cares and misbehaves, but the caller does
> not know how long the reflog is before starting to call
> get_revision() in a loop anyway, so it already has to deal with a
> case where it did .recno=20 and get_revision() did not return that
> many times.  So this probably is safe.

That corresponds to what I see when experimenting with different
reflogs.

> > +test_expect_success 'reflog containing non-commit sha1s displays
> > properly' '
> 
> In general, "properly" is a poor word to use in test description (or
> a commit log message or a bug report, for that matter), as the whole
> point of a test is to precisely define what is "proper".
> 
> And the code change declares that a proper thing to do is to omit
> non-commit entries without segfaulting, so something like
> 
>     s/displays properly/omits them/
> 
> perhaps?

I did find the test title a bit iffy but couldn't really figure out
why. What you're saying makes a lot of sense, will fix.
-- 
Dennis Kaarsemaker
www.kaarsemaker.net