Commit 2b2abcac authored by David Disseldorp's avatar David Disseldorp Committed by Ilya Dryomov
Browse files

ceph: fix listxattr vxattr buffer length calculation



ceph_listxattr() incorrectly returns a length based on the static
ceph_vxattrs_name_size() value, which only takes into account whether
vxattrs are hidden, ignoring vxattr.exists_cb().

When filling the xattr buffer ceph_listxattr() checks VXATTR_FLAG_HIDDEN
and vxattr.exists_cb(). If both are false, we return an incorrect
(oversize) length.

Fix this behaviour by always calculating the vxattrs length at runtime,
taking both vxattr.hidden and vxattr.exists_cb() into account.

This bug is only exposed with the new "ceph.snap.btime" vxattr, as all
other vxattrs with a non-null exists_cb also carry VXATTR_FLAG_HIDDEN.

Signed-off-by: default avatarDavid Disseldorp <ddiss@suse.de>
Reviewed-by: default avatar"Yan, Zheng" <zyan@redhat.com>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent 100cc610
Loading
Loading
Loading
Loading
+30 −24
Original line number Diff line number Diff line
@@ -924,10 +924,9 @@ ssize_t ceph_listxattr(struct dentry *dentry, char *names, size_t size)
	struct inode *inode = d_inode(dentry);
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct ceph_vxattr *vxattrs = ceph_inode_vxattrs(inode);
	u32 vir_namelen = 0;
	bool len_only = (size == 0);
	u32 namelen;
	int err;
	u32 len;
	int i;

	spin_lock(&ci->i_ceph_lock);
@@ -946,38 +945,45 @@ ssize_t ceph_listxattr(struct dentry *dentry, char *names, size_t size)
	err = __build_xattrs(inode);
	if (err < 0)
		goto out;
	/*
	 * Start with virtual dir xattr names (if any) (including
	 * terminating '\0' characters for each).
	 */
	vir_namelen = ceph_vxattrs_name_size(vxattrs);

	/* adding 1 byte per each variable due to the null termination */
	/* add 1 byte for each xattr due to the null termination */
	namelen = ci->i_xattrs.names_size + ci->i_xattrs.count;
	if (!len_only) {
		if (namelen > size) {
			err = -ERANGE;
	if (size && vir_namelen + namelen > size)
		goto out;

	err = namelen + vir_namelen;
	if (size == 0)
			goto out;

		}
		names = __copy_xattr_names(ci, names);
		size -= namelen;
	}


	/* virtual xattr names, too */
	err = namelen;
	if (vxattrs) {
		for (i = 0; vxattrs[i].name; i++) {
			if (!(vxattrs[i].flags & VXATTR_FLAG_HIDDEN) &&
			    !(vxattrs[i].exists_cb &&
			      !vxattrs[i].exists_cb(ci))) {
				len = sprintf(names, "%s", vxattrs[i].name);
				names += len + 1;
				err += len + 1;
			size_t this_len;

			if (vxattrs[i].flags & VXATTR_FLAG_HIDDEN)
				continue;
			if (vxattrs[i].exists_cb && !vxattrs[i].exists_cb(ci))
				continue;

			this_len = strlen(vxattrs[i].name) + 1;
			namelen += this_len;
			if (len_only)
				continue;

			if (this_len > size) {
				err = -ERANGE;
				goto out;
			}

			memcpy(names, vxattrs[i].name, this_len);
			names += this_len;
			size -= this_len;
		}
	}

	err = namelen;
out:
	spin_unlock(&ci->i_ceph_lock);
	return err;