History

ceph: hold extra reference to r_parent over life of request

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.

Jeff Layton committed 7 years ago

Commit 9c1c2b31 parent def9d271 file · +6 −2Browse files

Showing the changes to fs (1 of 1 file). Show all

Unified · Split

fs/ceph/mds_client.c+6−2
@@ -708,8 +708,10 @@ void ceph_mdsc_release_request(struct kref *kref) · 707 unmodified lines
708 /* avoid calling iput_final() in mds dispatch threads */708 /* avoid calling iput_final() in mds dispatch threads */
709 ceph_async_iput(req->r_inode);709 ceph_async_iput(req->r_inode);
710 }710 }
711 if (req->r_parent)711 if (req->r_parent) {
712 ceph_put_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN);712 ceph_put_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN);
713 ceph_async_iput(req->r_parent);
714 }
713 ceph_async_iput(req->r_target_inode);715 ceph_async_iput(req->r_target_inode);
714 if (req->r_dentry)716 if (req->r_dentry)
715 dput(req->r_dentry);717 dput(req->r_dentry);
@@ -2676,8 +2678,10 @@ int ceph_mdsc_submit_request(struct ceph_mds_client *mdsc, struct inode *dir, · 1960 unmodified lines
2676 /* take CAP_PIN refs for r_inode, r_parent, r_old_dentry */2678 /* take CAP_PIN refs for r_inode, r_parent, r_old_dentry */
2677 if (req->r_inode)2679 if (req->r_inode)
2678 ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN);2680 ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN);
2679 if (req->r_parent)2681 if (req->r_parent) {
2680 ceph_get_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN);2682 ceph_get_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN);
2683 ihold(req->r_parent);
2684 }
2681 if (req->r_old_dentry_dir)2685 if (req->r_old_dentry_dir)
2682 ceph_get_cap_refs(ceph_inode(req->r_old_dentry_dir),2686 ceph_get_cap_refs(ceph_inode(req->r_old_dentry_dir),
2683 CEPH_CAP_PIN);2687 CEPH_CAP_PIN);