| From fb3fff36538fb422ec4a809964e42fcc08acfa0e Mon Sep 17 00:00:00 2001 |
| From: Jeff Layton <jlayton@kernel.org> |
| Date: Wed, 3 Apr 2019 13:16:01 -0400 |
| Subject: [PATCH] ceph: hold extra reference to r_parent over life of request |
| |
| commit 9c1c2b35f1d94de8325344c2777d7ee67492db3b upstream. |
| |
| Currently, we just assume that it will stick around by virtue of the |
| submitter's reference, but later patches will allow the syscall to |
| return early and we can't rely on that reference at that point. |
| |
| While I'm not aware of any reports of it, Xiubo pointed out that this |
| may fix a use-after-free. If the wait for a reply times out or is |
| canceled via signal, and then the reply comes in after the syscall |
| returns, the client can end up trying to access r_parent without a |
| reference. |
| |
| Take an extra reference to the inode when setting r_parent and release |
| it when releasing the request. |
| |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Jeff Layton <jlayton@kernel.org> |
| Reviewed-by: "Yan, Zheng" <zyan@redhat.com> |
| Signed-off-by: Ilya Dryomov <idryomov@gmail.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c |
| index ef58306df674..925ce2ea7331 100644 |
| --- a/fs/ceph/mds_client.c |
| +++ b/fs/ceph/mds_client.c |
| @@ -694,8 +694,10 @@ void ceph_mdsc_release_request(struct kref *kref) |
| /* avoid calling iput_final() in mds dispatch threads */ |
| ceph_async_iput(req->r_inode); |
| } |
| - if (req->r_parent) |
| + if (req->r_parent) { |
| ceph_put_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN); |
| + ceph_async_iput(req->r_parent); |
| + } |
| ceph_async_iput(req->r_target_inode); |
| if (req->r_dentry) |
| dput(req->r_dentry); |
| @@ -2638,8 +2640,10 @@ int ceph_mdsc_submit_request(struct ceph_mds_client *mdsc, struct inode *dir, |
| /* take CAP_PIN refs for r_inode, r_parent, r_old_dentry */ |
| if (req->r_inode) |
| ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN); |
| - if (req->r_parent) |
| + if (req->r_parent) { |
| ceph_get_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN); |
| + ihold(req->r_parent); |
| + } |
| if (req->r_old_dentry_dir) |
| ceph_get_cap_refs(ceph_inode(req->r_old_dentry_dir), |
| CEPH_CAP_PIN); |
| -- |
| 2.7.4 |
| |