Commit f488138b authored by Vasily Gorbik's avatar Vasily Gorbik Committed by Chuck Lever
Browse files

NFSD: fix endianness issue in nfsd4_encode_fattr4

The nfs4 mount fails with EIO on 64-bit big endian architectures since
v6.7. The issue arises from employing a union in the nfsd4_encode_fattr4()
function to overlay a 32-bit array with a 64-bit values based bitmap,
which does not function as intended. Address the endianness issue by
utilizing bitmap_from_arr32() to copy 32-bit attribute masks into a
bitmap in an endianness-agnostic manner.

Cc: stable@vger.kernel.org
Fixes: fce7913b ("NFSD: Use a bitmask loop to encode FATTR4 results")
Link: https://bugs.launchpad.net/ubuntu/+source/nfs-utils/+bug/2060217


Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent a4833e3a
Loading
Loading
Loading
Loading
+23 −24
Original line number Diff line number Diff line
@@ -3490,11 +3490,13 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
		    struct dentry *dentry, const u32 *bmval,
		    int ignore_crossmnt)
{
	DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
	struct nfsd4_fattr_args args;
	struct svc_fh *tempfh = NULL;
	int starting_len = xdr->buf->len;
	__be32 *attrlen_p, status;
	int attrlen_offset;
	u32 attrmask[3];
	int err;
	struct nfsd4_compoundres *resp = rqstp->rq_resp;
	u32 minorversion = resp->cstate.minorversion;
@@ -3502,10 +3504,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
		.mnt	= exp->ex_path.mnt,
		.dentry	= dentry,
	};
	union {
		u32		attrmask[3];
		unsigned long	mask[2];
	} u;
	unsigned long bit;
	bool file_modified = false;
	u64 size = 0;
@@ -3521,20 +3519,19 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
	/*
	 * Make a local copy of the attribute bitmap that can be modified.
	 */
	memset(&u, 0, sizeof(u));
	u.attrmask[0] = bmval[0];
	u.attrmask[1] = bmval[1];
	u.attrmask[2] = bmval[2];
	attrmask[0] = bmval[0];
	attrmask[1] = bmval[1];
	attrmask[2] = bmval[2];

	args.rdattr_err = 0;
	if (exp->ex_fslocs.migrated) {
		status = fattr_handle_absent_fs(&u.attrmask[0], &u.attrmask[1],
						&u.attrmask[2], &args.rdattr_err);
		status = fattr_handle_absent_fs(&attrmask[0], &attrmask[1],
						&attrmask[2], &args.rdattr_err);
		if (status)
			goto out;
	}
	args.size = 0;
	if (u.attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
	if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
		status = nfsd4_deleg_getattr_conflict(rqstp, d_inode(dentry),
					&file_modified, &size);
		if (status)
@@ -3553,16 +3550,16 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,

	if (!(args.stat.result_mask & STATX_BTIME))
		/* underlying FS does not offer btime so we can't share it */
		u.attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
	if ((u.attrmask[0] & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
		attrmask[1] &= ~FATTR4_WORD1_TIME_CREATE;
	if ((attrmask[0] & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
			FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
	    (u.attrmask[1] & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
	    (attrmask[1] & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
		       FATTR4_WORD1_SPACE_TOTAL))) {
		err = vfs_statfs(&path, &args.statfs);
		if (err)
			goto out_nfserr;
	}
	if ((u.attrmask[0] & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) &&
	if ((attrmask[0] & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) &&
	    !fhp) {
		tempfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
		status = nfserr_jukebox;
@@ -3577,10 +3574,10 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
		args.fhp = fhp;

	args.acl = NULL;
	if (u.attrmask[0] & FATTR4_WORD0_ACL) {
	if (attrmask[0] & FATTR4_WORD0_ACL) {
		err = nfsd4_get_nfs4_acl(rqstp, dentry, &args.acl);
		if (err == -EOPNOTSUPP)
			u.attrmask[0] &= ~FATTR4_WORD0_ACL;
			attrmask[0] &= ~FATTR4_WORD0_ACL;
		else if (err == -EINVAL) {
			status = nfserr_attrnotsupp;
			goto out;
@@ -3592,17 +3589,17 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,

#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
	args.context = NULL;
	if ((u.attrmask[2] & FATTR4_WORD2_SECURITY_LABEL) ||
	     u.attrmask[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
	if ((attrmask[2] & FATTR4_WORD2_SECURITY_LABEL) ||
	     attrmask[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
		if (exp->ex_flags & NFSEXP_SECURITY_LABEL)
			err = security_inode_getsecctx(d_inode(dentry),
						&args.context, &args.contextlen);
		else
			err = -EOPNOTSUPP;
		args.contextsupport = (err == 0);
		if (u.attrmask[2] & FATTR4_WORD2_SECURITY_LABEL) {
		if (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL) {
			if (err == -EOPNOTSUPP)
				u.attrmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
				attrmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
			else if (err)
				goto out_nfserr;
		}
@@ -3610,8 +3607,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */

	/* attrmask */
	status = nfsd4_encode_bitmap4(xdr, u.attrmask[0],
				      u.attrmask[1], u.attrmask[2]);
	status = nfsd4_encode_bitmap4(xdr, attrmask[0], attrmask[1],
				      attrmask[2]);
	if (status)
		goto out;

@@ -3620,7 +3617,9 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
	attrlen_p = xdr_reserve_space(xdr, XDR_UNIT);
	if (!attrlen_p)
		goto out_resource;
	for_each_set_bit(bit, (const unsigned long *)&u.mask,
	bitmap_from_arr32(attr_bitmap, attrmask,
			  ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
	for_each_set_bit(bit, attr_bitmap,
			 ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)) {
		status = nfsd4_enc_fattr4_encode_ops[bit](xdr, &args);
		if (status != nfs_ok)