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
708708 /* avoid calling iput_final() in mds dispatch threads */
709709 ceph_async_iput(req->r_inode);
710710 }
711− if (req->r_parent)
711+ if (req->r_parent) {
712712 ceph_put_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN);
713+ ceph_async_iput(req->r_parent);
714+ }
713715 ceph_async_iput(req->r_target_inode);
714716 if (req->r_dentry)
715717 dput(req->r_dentry);
@@ -2676,8 +2678,10 @@ int ceph_mdsc_submit_request(struct ceph_mds_client *mdsc, struct inode *dir, · 1960 unmodified lines
26762678 /* take CAP_PIN refs for r_inode, r_parent, r_old_dentry */
26772679 if (req->r_inode)
26782680 ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN);
2679− if (req->r_parent)
2681+ if (req->r_parent) {
26802682 ceph_get_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN);
2683+ ihold(req->r_parent);
2684+ }
26812685 if (req->r_old_dentry_dir)
26822686 ceph_get_cap_refs(ceph_inode(req->r_old_dentry_dir),
26832687 CEPH_CAP_PIN);