From: Derrick Stolee Date: Thu, 13 Sep 2018 16:38:16 GMT Subject: Re: [PATCH v2 1/1] commit-reach: properly peel tags Message-ID: <8d6061de-1654-577c-40c6-211dbd03aa36@gmail.com> In-Reply-To: <4bf21204ddf8b9daa823db9954ddde98ab264a1b.1536855032.git.gitgitgadget@gmail.com> On 9/13/2018 12:10 PM, Derrick Stolee via GitGitGadget wrote: > From: Derrick Stolee > > The can_all_from_reach_with_flag() algorithm was refactored in 4fbcca4e > "commit-reach: make can_all_from_reach... linear" but incorrectly > assumed that all objects provided were commits. During a fetch > negotiation, ok_to_give_up() in upload-pack.c may provide unpeeled tags > to the 'from' array. The current code creates a segfault. > > Add a direct call to can_all_from_reach_with_flag() in 'test-tool reach' > and add a test in t6600-test-reach.sh that demonstrates this segfault. > > Correct the issue by peeling tags when investigating the initial list > of objects in the 'from' array. > > Signed-off-by: Jeff King > Signed-off-by: Derrick Stolee > --- > commit-reach.c | 33 ++++++++++++++++++++++++++------- > t/helper/test-reach.c | 22 +++++++++++++++++----- > t/t6600-test-reach.sh | 30 ++++++++++++++++++++++++++++-- > 3 files changed, 71 insertions(+), 14 deletions(-) > > diff --git a/commit-reach.c b/commit-reach.c > index 86715c103c..4048a2132a 100644 > --- a/commit-reach.c > +++ b/commit-reach.c > @@ -544,20 +544,39 @@ int can_all_from_reach_with_flag(struct object_array *from, > { > struct commit **list = NULL; > int i; > + int nr_commits; > int result = 1; > > ALLOC_ARRAY(list, from->nr); > + nr_commits = 0; > for (i = 0; i < from->nr; i++) { > - list[i] = (struct commit *)from->objects[i].item; > + struct object *from_one = from->objects[i].item; > > - if (parse_commit(list[i]) || > - list[i]->generation < min_generation) > - return 0; > + if (!from_one || from_one->flags & assign_flag) > + continue; > + > + from_one = deref_tag(the_repository, from_one, > + "a from object", 0); > + if (!from_one || from_one->type != OBJ_COMMIT) { > + /* no way to tell if this is reachable by > + * looking at the ancestry chain alone, so > + * leave a note to ourselves not to worry about > + * this object anymore. > + */ > + from->objects[i].item->flags |= assign_flag; > + continue; > + } > + > + list[nr_commits] = (struct commit *)from_one; > + if (parse_commit(list[nr_commits]) || > + list[nr_commits]->generation < min_generation) > + return 0; /* is this a leak? */ Of course, after sending v2, I see this comment. This is a leak of 'list' and should be fixed. Not only is it a leak here, it is also a leak in the 'cleanup' section. I'll squash the following into v3, but I'll let v2 simmer for review before rerolling. diff --git a/commit-reach.c b/commit-reach.c index 4048a2132a..c457d8d85f 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -569,8 +569,11 @@ int can_all_from_reach_with_flag(struct object_array *from,                 list[nr_commits] = (struct commit *)from_one;                 if (parse_commit(list[nr_commits]) || -                   list[nr_commits]->generation < min_generation) -                       return 0; /* is this a leak? */ +                   list[nr_commits]->generation < min_generation) { +                       result = 0; +                       goto cleanup; +               } +                 nr_commits++;         } @@ -623,6 +626,7 @@ int can_all_from_reach_with_flag(struct object_array *from,                 clear_commit_marks(list[i], RESULT);                 clear_commit_marks(list[i], assign_flag);         } +       free(list);         return result;  }