Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 315f2408 authored by Yan, Zheng's avatar Yan, Zheng Committed by Ilya Dryomov
Browse files

ceph: fix security xattr deadlock



When security is enabled, security module can call filesystem's
getxattr/setxattr callbacks during d_instantiate(). For cephfs,
d_instantiate() is usually called by MDS' dispatch thread, while
handling MDS reply. If the MDS reply does not include xattrs and
corresponding caps, getxattr/setxattr need to send a new request
to MDS and waits for the reply. This makes MDS' dispatch sleep,
nobody handles later MDS replies.

The fix is make sure lookup/atomic_open reply include xattrs and
corresponding caps. So getxattr can be handled by cached xattrs.
This requires some modification to both MDS and request message.
(Client tells MDS what caps it wants; MDS encodes proper caps in
the reply)

Smack security module may call setxattr during d_instantiate().
Unlike getxattr, we can't force MDS to issue CEPH_CAP_XATTR_EXCL
to us. So just make setxattr return error when called by MDS'
dispatch thread.

Signed-off-by: default avatarYan, Zheng <zyan@redhat.com>
parent 29dccfa5
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -624,6 +624,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
	struct ceph_mds_client *mdsc = fsc->mdsc;
	struct ceph_mds_request *req;
	int op;
	int mask;
	int err;

	dout("lookup %p dentry %p '%pd'\n",
@@ -666,8 +667,12 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
		return ERR_CAST(req);
	req->r_dentry = dget(dentry);
	req->r_num_caps = 2;
	/* we only need inode linkage */
	req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE);

	mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED;
	if (ceph_security_xattr_wanted(dir))
		mask |= CEPH_CAP_XATTR_SHARED;
	req->r_args.getattr.mask = cpu_to_le32(mask);

	req->r_locked_dir = dir;
	err = ceph_mdsc_do_request(mdsc, NULL, req);
	err = ceph_handle_snapdir(req, dentry, err);
+13 −0
Original line number Diff line number Diff line
@@ -71,12 +71,18 @@ static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
	inode = ceph_find_inode(sb, vino);
	if (!inode) {
		struct ceph_mds_request *req;
		int mask;

		req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
					       USE_ANY_MDS);
		if (IS_ERR(req))
			return ERR_CAST(req);

		mask = CEPH_STAT_CAP_INODE;
		if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
			mask |= CEPH_CAP_XATTR_SHARED;
		req->r_args.getattr.mask = cpu_to_le32(mask);

		req->r_ino1 = vino;
		req->r_num_caps = 1;
		err = ceph_mdsc_do_request(mdsc, NULL, req);
@@ -128,6 +134,7 @@ static struct dentry *__get_parent(struct super_block *sb,
	struct ceph_mds_request *req;
	struct inode *inode;
	struct dentry *dentry;
	int mask;
	int err;

	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
@@ -144,6 +151,12 @@ static struct dentry *__get_parent(struct super_block *sb,
			.snap = CEPH_NOSNAP,
		};
	}

	mask = CEPH_STAT_CAP_INODE;
	if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
		mask |= CEPH_CAP_XATTR_SHARED;
	req->r_args.getattr.mask = cpu_to_le32(mask);

	req->r_num_caps = 1;
	err = ceph_mdsc_do_request(mdsc, NULL, req);
	inode = req->r_target_inode;
+7 −0
Original line number Diff line number Diff line
@@ -300,6 +300,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
	struct ceph_mds_request *req;
	struct dentry *dn;
	struct ceph_acls_info acls = {};
       int mask;
	int err;

	dout("atomic_open %p dentry %p '%pd' %s flags %d mode 0%o\n",
@@ -335,6 +336,12 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
			acls.pagelist = NULL;
		}
	}

       mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED;
       if (ceph_security_xattr_wanted(dir))
               mask |= CEPH_CAP_XATTR_SHARED;
       req->r_args.open.mask = cpu_to_le32(mask);

	req->r_locked_dir = dir;           /* caller holds dir->i_mutex */
	err = ceph_mdsc_do_request(mdsc,
				   (flags & (O_CREAT|O_TRUNC)) ? dir : NULL,
+14 −4
Original line number Diff line number Diff line
@@ -1389,7 +1389,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
	struct qstr dname;
	struct dentry *dn;
	struct inode *in;
	int err = 0, ret, i;
	int err = 0, skipped = 0, ret, i;
	struct inode *snapdir = NULL;
	struct ceph_mds_request_head *rhead = req->r_request->front.iov_base;
	struct ceph_dentry_info *di;
@@ -1501,7 +1501,17 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
		}

		if (d_really_is_negative(dn)) {
			struct dentry *realdn = splice_dentry(dn, in);
			struct dentry *realdn;

			if (ceph_security_xattr_deadlock(in)) {
				dout(" skip splicing dn %p to inode %p"
				     " (security xattr deadlock)\n", dn, in);
				iput(in);
				skipped++;
				goto next_item;
			}

			realdn = splice_dentry(dn, in);
			if (IS_ERR(realdn)) {
				err = PTR_ERR(realdn);
				d_drop(dn);
@@ -1518,7 +1528,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
				    req->r_session,
				    req->r_request_started);

		if (err == 0 && cache_ctl.index >= 0) {
		if (err == 0 && skipped == 0 && cache_ctl.index >= 0) {
			ret = fill_readdir_cache(d_inode(parent), dn,
						 &cache_ctl, req);
			if (ret < 0)
@@ -1529,7 +1539,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
			dput(dn);
	}
out:
	if (err == 0) {
	if (err == 0 && skipped == 0) {
		req->r_did_prepopulate = true;
		req->r_readdir_cache_idx = cache_ctl.index;
	}
+2 −0
Original line number Diff line number Diff line
@@ -2540,6 +2540,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)

	/* insert trace into our cache */
	mutex_lock(&req->r_fill_mutex);
	current->journal_info = req;
	err = ceph_fill_trace(mdsc->fsc->sb, req, req->r_session);
	if (err == 0) {
		if (result == 0 && (req->r_op == CEPH_MDS_OP_READDIR ||
@@ -2547,6 +2548,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
			ceph_readdir_prepopulate(req, req->r_session);
		ceph_unreserve_caps(mdsc, &req->r_caps_reservation);
	}
	current->journal_info = NULL;
	mutex_unlock(&req->r_fill_mutex);

	up_read(&mdsc->snap_rwsem);
Loading