Blob Blame History Raw
From: Luis Henriques <lhenriques@suse.com>
Date: Tue, 30 May 2017 19:20:22 +0100
Subject: ceph: memory leak in ceph_direct_read_write callback
Patch-mainline: Never, it fixes SUSE-specific code that has never been
   merged upstream
References: bsc#1041810

Running xfstest generic/036 with kmemleak enabled shows several suspected
leaks:

unreferenced object 0xffff880077b86168 (size 8):
  comm "a.out", pid 231, jiffies 4294898530 (age 43.656s)
  hex dump (first 8 bytes):
    00 3b 06 00 00 ea ff ff                          .;......
  backtrace:
    [<ffffffff81589f3e>] kmemleak_alloc+0x4e/0xb0
    [<ffffffff8111cb24>] __kmalloc+0x134/0x1b0
    [<ffffffff812d7045>] ceph_direct_read_write+0x3b5/0xd70
    [<ffffffff812d86db>] ceph_write_iter+0x3eb/0xd80
    [<ffffffff81168c70>] aio_run_iocb+0x220/0x2a0
    [<ffffffff8116a346>] do_io_submit+0x296/0x4b0
    [<ffffffff8116a570>] SyS_io_submit+0x10/0x20
    [<ffffffff8158e517>] entry_SYSCALL_64_fastpath+0x12/0x66
    [<ffffffffffffffff>] 0xffffffffffffffff

ceph_direct_read_write() allocates an array of struct pages using
dio_get_pages_alloc() and never frees it -- the ceph_aio_complete_req
callback is supposed to do that.  However, this function is retrieving the
ceph_osd_data struct with osd_req_op_extent_osd_response_data() only, and
ignoring the fact that the operation could have been a CEPH_OSD_OP_WRITE*.

This patch verifies uses the correct function to initialize this struct
after checking aio_req->write.

Reviewed-by: David Disseldorp <ddiss@suse.de>
Signed-off-by: Luis Henriques <lhenriques@suse.com>
---
 fs/ceph/file.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index b51d0548bd9e..e4327fc778b2 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -695,10 +695,15 @@ static void ceph_aio_complete_req(struct ceph_osd_request *req)
 	int rc = req->r_result;
 	struct inode *inode = req->r_inode;
 	struct ceph_aio_request *aio_req = req->r_priv;
-	struct ceph_osd_data *osd_data = osd_req_op_extent_osd_response_data(req, 0);
-	int num_pages = calc_pages_for((u64)osd_data->alignment,
-				       osd_data->length);
+	struct ceph_osd_data *osd_data;
+	int num_pages;
 
+	if (aio_req->write)
+		osd_data = osd_req_op_extent_osd_request_data(req, 0);
+	else
+		osd_data = osd_req_op_extent_osd_response_data(req, 0);
+	num_pages = calc_pages_for((u64)osd_data->alignment,
+				   osd_data->length);
 	dout("ceph_aio_complete_req %p rc %d bytes %llu\n",
 	     inode, rc, osd_data->length);