ceph: fix buffer free while holding i_ceph_lock in fill_inode() [Linux 4.19.72]

This Linux kernel change "ceph: fix buffer free while holding i_ceph_lock in fill_inode()" is included in the Linux 4.19.72 release. This change is authored by Luis Henriques <lhenriques [at] suse.com> on Fri Jul 19 15:32:22 2019 +0100. The commit for this change in Linux stable tree is b84817d (patch) which is from upstream commit af8a85a. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream af8a85a.

ceph: fix buffer free while holding i_ceph_lock in fill_inode()

[ Upstream commit af8a85a41734f37b67ba8ce69d56b685bee4ac48 ]

Calling ceph_buffer_put() in fill_inode() may result in freeing the
i_xattrs.blob buffer while holding the i_ceph_lock.  This can be fixed by
postponing the call until later, when the lock is released.

The following backtrace was triggered by fstests generic/070.

  BUG: sleeping function called from invalid context at mm/vmalloc.c:2283
  in_atomic(): 1, irqs_disabled(): 0, pid: 3852, name: kworker/0:4
  6 locks held by kworker/0:4/3852:
   #0: 000000004270f6bb ((wq_completion)ceph-msgr){+.+.}, at: process_one_work+0x1b8/0x5f0
   #1: 00000000eb420803 ((work_completion)(&(&con->work)->work)){+.+.}, at: process_one_work+0x1b8/0x5f0
   #2: 00000000be1c53a4 (&s->s_mutex){+.+.}, at: dispatch+0x288/0x1476
   #3: 00000000559cb958 (&mdsc->snap_rwsem){++++}, at: dispatch+0x2eb/0x1476
   #4: 000000000d5ebbae (&req->r_fill_mutex){+.+.}, at: dispatch+0x2fc/0x1476
   #5: 00000000a83d0514 (&(&ci->i_ceph_lock)->rlock){+.+.}, at: fill_inode.isra.0+0xf8/0xf70
  CPU: 0 PID: 3852 Comm: kworker/0:4 Not tainted 5.2.0+ #441
  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58-prebuilt.qemu.org 04/01/2014
  Workqueue: ceph-msgr ceph_con_workfn
  Call Trace:
   ? dispatch+0x2eb/0x1476
   ? __mutex_unlock_slowpath+0x4d/0x2a0
   ? process_one_work+0x1b8/0x5f0
   ? process_one_work+0x5f0/0x5f0
   ? kthread_park+0x90/0x90

Signed-off-by: Luis Henriques <lhenriques@suse.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>

There are 7 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 fs/ceph/inode.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 3e518c2..11f1943 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -742,6 +742,7 @@ static int fill_inode(struct inode *inode, struct page *locked_page,
    int issued, new_issued, info_caps;
    struct timespec64 mtime, atime, ctime;
    struct ceph_buffer *xattr_blob = NULL;
+   struct ceph_buffer *old_blob = NULL;
    struct ceph_string *pool_ns = NULL;
    struct ceph_cap *new_cap = NULL;
    int err = 0;
@@ -878,7 +879,7 @@ static int fill_inode(struct inode *inode, struct page *locked_page,
    if ((ci->i_xattrs.version == 0 || !(issued & CEPH_CAP_XATTR_EXCL))  &&
        le64_to_cpu(info->xattr_version) > ci->i_xattrs.version) {
        if (ci->i_xattrs.blob)
-           ceph_buffer_put(ci->i_xattrs.blob);
+           old_blob = ci->i_xattrs.blob;
        ci->i_xattrs.blob = xattr_blob;
        if (xattr_blob)
@@ -1017,8 +1018,8 @@ static int fill_inode(struct inode *inode, struct page *locked_page,
    if (new_cap)
        ceph_put_cap(mdsc, new_cap);
-   if (xattr_blob)
-       ceph_buffer_put(xattr_blob);
+   ceph_buffer_put(old_blob);
+   ceph_buffer_put(xattr_blob);
    return err;

Leave a Reply

Your email address will not be published. Required fields are marked *