Re: [PATCH v3 12/25] split_commit_in_progress(): fix memory leak

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi René,

On Wed, 3 May 2017, René Scharfe wrote:

> Am 02.05.2017 um 18:02 schrieb Johannes Schindelin:
> > Reported via Coverity.
> > 
> > Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx>
> > ---
> >   wt-status.c | 7 ++++++-
> >   1 file changed, 6 insertions(+), 1 deletion(-)
> > 
> > diff --git a/wt-status.c b/wt-status.c
> > index 0a6e16dbe0f..1f3f6bcb980 100644
> > --- a/wt-status.c
> > +++ b/wt-status.c
> > @@ -1088,8 +1088,13 @@ static int split_commit_in_progress(struct wt_status
> > *s)
> >    char *rebase_orig_head =
> >    read_line_from_git_path("rebase-merge/orig-head");
> >   
> >   	if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
> > -	    !s->branch || strcmp(s->branch, "HEAD"))
> > +	    !s->branch || strcmp(s->branch, "HEAD")) {
> > +		free(head);
> > +		free(orig_head);
> > +		free(rebase_amend);
> > +		free(rebase_orig_head);
> >   		return split_in_progress;
> > +	}
> >   
> >    if (!strcmp(rebase_amend, rebase_orig_head)) {
> >     if (strcmp(head, rebase_amend))
> > 
> 
> The return line could be replaced by "; else" to achieve the same
> result, without duplicating the free calls.

True. It is much harder to explain it that way, though: the context looks
like this:

static int split_commit_in_progress(struct wt_status *s)
 {
        int split_in_progress = 0;
        char *head = read_line_from_git_path("HEAD");
        char *orig_head = read_line_from_git_path("ORIG_HEAD");
        char *rebase_amend = read_line_from_git_path("rebase-merge/amend");
        char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");

        if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
-           !s->branch || strcmp(s->branch, "HEAD"))
+           !s->branch || strcmp(s->branch, "HEAD")) {
+               free(head);
+               free(orig_head);
+               free(rebase_amend);
+               free(rebase_orig_head);
                return split_in_progress;
+       }

        if (!strcmp(rebase_amend, rebase_orig_head)) {
                if (strcmp(head, rebase_amend))
                        split_in_progress = 1;
        } else if (strcmp(orig_head, rebase_orig_head)) {
                split_in_progress = 1;
        }

        if (!s->amend && !s->nowarn && !s->workdir_dirty)
                split_in_progress = 0;

        free(head);
        free(orig_head);
        free(rebase_amend);
        free(rebase_orig_head);
        return split_in_progress;
 }

So as you see, if we make the change you suggested, the next if() is hit
which possibly sets split_in_progress = 0.

The thing is: this variable has been initialized to 0 in the beginning! So
this conditional assignment would be a noop, unless any of the code paths
before this conditional modified split_in_progress.

After you fully wrapped your head around this code, I am sure you agree
that the code is unnecessarily confusing to begin with: why do we go out
of our way to allocate and read all those strings, compare them to figure
out whether there is a split in progress, just to look at bits in the
wt_status struct (that we had available from the beginning) to potentially
undo all of that.

What's worse, I cannot even find a logical explanation why the code is so
confusing, as it has been added as it is today in commit 2d1ccebae41
(status: better advices when splitting a commit (during rebase -i),
2012-06-10).

So I think this function really wants to be fixed more fully (although I
feel bad for inserting such a non-trivial fix into a patch series
otherwise populated by trivial memory/resource leak plugs):

-- snipsnap --
Subject: [PATCH] squash! split_commit_in_progress(): fix memory leak

split_commit_in_progress(): simplify & fix memory leak

This function did a whole lot of unnecessary work, such as reading in
four files just to figure out that, oh, hey, we do not need to look at
them after all because the HEAD is not detached.

Simplify the entire function to return early when possible, to read in
the files only when necessary, and to release the allocated memory
always (there was a leak, reported via Coverity, where we failed to
release the allocated strings if the HEAD is not detached).

Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx>
---
 wt-status.c | 39 +++++++++++++++++----------------------
 1 file changed, 17 insertions(+), 22 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index 1f3f6bcb980..117ac8cfb01 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1082,34 +1082,29 @@ static char *read_line_from_git_path(const char *filename)
 static int split_commit_in_progress(struct wt_status *s)
 {
 	int split_in_progress = 0;
-	char *head = read_line_from_git_path("HEAD");
-	char *orig_head = read_line_from_git_path("ORIG_HEAD");
-	char *rebase_amend = read_line_from_git_path("rebase-merge/amend");
-	char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
-
-	if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
-	    !s->branch || strcmp(s->branch, "HEAD")) {
-		free(head);
-		free(orig_head);
-		free(rebase_amend);
-		free(rebase_orig_head);
-		return split_in_progress;
-	}
-
-	if (!strcmp(rebase_amend, rebase_orig_head)) {
-		if (strcmp(head, rebase_amend))
-			split_in_progress = 1;
-	} else if (strcmp(orig_head, rebase_orig_head)) {
-		split_in_progress = 1;
-	}
+	char *head, *orig_head, *rebase_amend, *rebase_orig_head;
+
+	if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
+	    !s->branch || strcmp(s->branch, "HEAD"))
+		return 0;
 
-	if (!s->amend && !s->nowarn && !s->workdir_dirty)
-		split_in_progress = 0;
+	head = read_line_from_git_path("HEAD");
+	orig_head = read_line_from_git_path("ORIG_HEAD");
+	rebase_amend = read_line_from_git_path("rebase-merge/amend");
+	rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
+
+	if (!head || !orig_head || !rebase_amend || !rebase_orig_head)
+		; /* fall through, no split in progress */
+	else if (!strcmp(rebase_amend, rebase_orig_head))
+		split_in_progress = !!strcmp(head, rebase_amend);
+	else if (strcmp(orig_head, rebase_orig_head))
+		split_in_progress = 1;
 
 	free(head);
 	free(orig_head);
 	free(rebase_amend);
 	free(rebase_orig_head);
+
 	return split_in_progress;
 }
 
-- 
2.12.2.windows.2.800.gede8f145e06

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]