On Thu, Apr 11, 2024 at 11:45:57AM +0200, Vasily Gorbik wrote: > 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@xxxxxxxxxxxxxxx> > Fixes: fce7913b13d0 ("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@xxxxxxxxxxxxx> Thank you for the bug fix! Applied to nfsd-fixes (for v6.9-rc). > --- > fs/nfsd/nfs4xdr.c | 47 +++++++++++++++++++++++------------------------ > 1 file changed, 23 insertions(+), 24 deletions(-) > > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c > index 10439d569d9c..85d43b3249f9 100644 > --- a/fs/nfsd/nfs4xdr.c > +++ b/fs/nfsd/nfs4xdr.c > @@ -3519,11 +3519,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; > @@ -3531,10 +3533,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; > @@ -3550,20 +3548,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) > @@ -3582,16 +3579,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; > @@ -3606,10 +3603,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; > @@ -3621,17 +3618,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; > } > @@ -3639,8 +3636,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; > > @@ -3649,7 +3646,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) > -- > 2.41.0 -- Chuck Lever