diff mbox

ext4: Buffer Head Leak In mmp

Message ID CAJ-febOhan58G=Aw+-CD6OD2iKuFY3dxkzBfVMCwBd6vXs1rFg@mail.gmail.com
State Superseded, archived
Headers show

Commit Message

vikram.jadhav07 Jan. 20, 2016, 3:48 p.m. UTC
Description:  ext4: Buffer Head Leak In mmp

There is memory leak as both caller function kmmpd() and callee
read_mmp_block() not releasing bh_check  (i.e buffer_head).
Given patch fixes this problem.

Signed-off-by: Jadhav Vikram <vikramjadhavpucsd2007@gmail.com>

Patch:

Comments

Andreas Dilger Feb. 8, 2016, 9:40 p.m. UTC | #1
On Jan 20, 2016, at 8:48 AM, vikram.jadhav07 <vikramjadhavpucsd2007@gmail.com> wrote:
> 
> Description:  ext4: Buffer Head Leak In mmp
> 
> There is memory leak as both caller function kmmpd() and callee
> read_mmp_block() not releasing bh_check  (i.e buffer_head).
> Given patch fixes this problem.

I was going to give this a Signed-off-by, since it matches patches that we
have included into the Lustre ext4 patch series, but it seems the code is
different in newer kernels because of the metadata checksum feature and
could be improved a bit further.

> Signed-off-by: Jadhav Vikram <vikramjadhavpucsd2007@gmail.com>
> 
> Patch:
> =====
> --- /root/linux.orig/fs/ext4/mmp.c      2016-01-18 07:23:06.227519005 +0530
> +++ /root/linux/fs/ext4/mmp.c   2016-01-18 07:31:18.545518552 +0530
> @@ -98,12 +98,17 @@
>         }
> 
>         mmp = (struct mmp_struct *)((*bh)->b_data);
> +       if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC) {
>                ret = -EFSCORRUPTED;
> +               brelse(*bh);
> +               *bh = NULL;
> +       } else if (!ext4_mmp_csum_verify(sb, mmp)) {
>                ret = -EFSBADCRC;
> +               brelse(*bh);
> +               *bh = NULL;
> +       } else {
>                return 0;
> +       }

Having the normal return be at the end of the error handling is non-standard
and would be better structured to have "goto out_bh;" in each of the
error cases, and then the "naked" return at the end.  That avoids duplicating
the error handling, and will avoid bugs/leaks in the future if this code is
changed to add more failure conditions or returns.


	mmp = (struct mmp_struct *)((*bh)->b_data);
	if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC) {
		ret = -EFSCORRUPTED;
		goto out_bh;
	}
	if (!ext4_mmp_csum_verify(sb, mmp)) {
		ret = -EFSBADCRC;
		goto out_bh;
	}

	return 0;

out_bh:
	brelse(*bh);
	*bh = NULL;
warn_exit:
	ext4_warning(sb, "Error %d while reading MMP block %llu",
		     ret, mmp_block);
	return ret;

> @@ -225,6 +230,7 @@
>                                             "The filesystem seems to have been"
>                                             " multiply mounted.");
>                                ext4_error(sb, "abort");
> +                               put_bh(bh_check);
>                                goto failed;
>                        }
>                        put_bh(bh_check);

I was going to suggest something similar here to consolidate the "put_bh()"
calls, but it doesn't look like that can be done easily.

Instead I found a bug because this isn't setting s_mmp_tsk = NULL as the
other error branches are doing, which will lead to the filesystem cleanup
code trying to stop this thread again.  Could you please remove the duplicate
"s_mmp_tsk = NULL" assignments.  I also see that the error handling is still
not quite correct because "retval" is not set in this branch, which further
emphasizes the benefits of using the "goto NNN" style of error handling.
It should be something like:

			if (retval) {
				ext4_error(sb, "error reading MMP data: %d",
					   retval);
				goto out_tsk;
			}
			if (mmp->mmp_seq != ...) {
				dump_mmp_msg(sb, mmp_check, ...);
				ext4_error(sb, "abort due to multiple mount");
				put_bh(bh_check);
				retval = -EBUSY;
				goto out_tsk;
			}
			put_bh(bh_check);
		}

		:
		:
	}

	:
	:

out_tsk:
	EXT4_SB(sb)->s_mmp_tsk = NULL;
out_free:
	kfree(data);
	brelse(bh);
	return retval;
}

Note "failed:" is renamed to "out_free:" since that code path is also used
in non-failure cases, for normal thread exit, and also to ensure that the
other two cases that explicitly set s_mmp_tsk = NULL and jumped to "failed:"
will also be cleaned up.

Cheers, Andreas
diff mbox

Patch

=====
--- /root/linux.orig/fs/ext4/mmp.c      2016-01-18 07:23:06.227519005 +0530
+++ /root/linux/fs/ext4/mmp.c   2016-01-18 07:31:18.545518552 +0530
@@ -98,12 +98,17 @@ 
        }

        mmp = (struct mmp_struct *)((*bh)->b_data);
-       if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC)
+       if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC) {
                ret = -EFSCORRUPTED;
-       else if (!ext4_mmp_csum_verify(sb, mmp))
+               brelse(*bh);
+               *bh = NULL;
+       } else if (!ext4_mmp_csum_verify(sb, mmp)) {
                ret = -EFSBADCRC;
-       else
+               brelse(*bh);
+               *bh = NULL;
+       } else {
                return 0;
+       }

 warn_exit:
        ext4_warning(sb, "Error %d while reading MMP block %llu",
@@ -225,6 +230,7 @@ 
                                             "The filesystem seems to have been"
                                             " multiply mounted.");
                                ext4_error(sb, "abort");
+                               put_bh(bh_check);
                                goto failed;
                        }
                        put_bh(bh_check);