Skip to content

Commit

Permalink
NFSD: fix endianness issue in nfsd4_encode_fattr4
Browse files Browse the repository at this point in the history
[ Upstream commit f488138 ]

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: fce7913 ("NFSD: Use a bitmask loop to encode FATTR4 results")
Link: https://bugs.launchpad.net/ubuntu/+source/nfs-utils/+bug/2060217
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Vasily Gorbik authored and gregkh committed Apr 27, 2024
1 parent 8af499b commit 0ff4640
Showing 1 changed file with 23 additions and 24 deletions.
47 changes: 23 additions & 24 deletions fs/nfsd/nfs4xdr.c
Expand Up @@ -3490,22 +3490,20 @@ 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;
struct path path = {
.mnt = exp->ex_path.mnt,
.dentry = dentry,
};
union {
u32 attrmask[3];
unsigned long mask[2];
} u;
unsigned long bit;

WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1);
Expand All @@ -3519,20 +3517,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));
if (status)
goto out;
Expand All @@ -3547,16 +3544,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;
Expand All @@ -3571,10 +3568,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;
Expand All @@ -3586,26 +3583,26 @@ 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;
}
}
#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;

Expand All @@ -3614,7 +3611,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)
Expand Down

0 comments on commit 0ff4640

Please sign in to comment.