git: 32fbc5d824f5 - main - nfs: don't truncate directory cookies to 32-bits in the NFS server

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Thu, 16 Dec 2021 03:55:02 UTC
The branch main has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=32fbc5d824f51f97220bc5c61a23e0bf3ff2b470

commit 32fbc5d824f51f97220bc5c61a23e0bf3ff2b470
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2021-12-13 03:57:14 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2021-12-16 03:54:57 +0000

    nfs: don't truncate directory cookies to 32-bits in the NFS server
    
    In NFSv2, the directory cookie was 32-bits.  NFSv3 widened it to
    64-bits and SVN r22521 widened the corresponding argument in
    VOP_READDIR, but FreeBSD's NFS server continued to treat the cookies as
    32-bits, and 0-extended to fill the field on the wire.  Nobody ever
    noticed, because every in-tree file system generates cookies that fit
    comfortably within 32-bits.
    
    Also, have better type safety for txdr_hyper.  Turn it into an inline
    function that type-checks its arguments.  Prevents warnings about
    shift-count-overflow.
    
    PR:             260375
    MFC after:      2 weeks
    Reviewed by:    rmacklem
    Differential Revision: https://reviews.freebsd.org/D33404
---
 sys/fs/nfs/xdr_subs.h           | 10 ++++++----
 sys/fs/nfsserver/nfs_nfsdport.c | 13 ++++++-------
 sys/fs/nfsserver/nfs_nfsdsubs.c |  6 +++---
 sys/nfs/xdr_subs.h              | 12 +++++++-----
 4 files changed, 22 insertions(+), 19 deletions(-)

diff --git a/sys/fs/nfs/xdr_subs.h b/sys/fs/nfs/xdr_subs.h
index 54c7d914983e..9a6d4608d5f1 100644
--- a/sys/fs/nfs/xdr_subs.h
+++ b/sys/fs/nfs/xdr_subs.h
@@ -93,9 +93,11 @@
         ((((u_quad_t)ntohl(((u_int32_t *)(f))[0])) << 32) |		\
 	 (u_quad_t)(ntohl(((u_int32_t *)(f))[1])))
 
-#define	txdr_hyper(f, t) do {						\
-	((u_int32_t *)(t))[0] = htonl((u_int32_t)((f) >> 32));		\
-	((u_int32_t *)(t))[1] = htonl((u_int32_t)((f) & 0xffffffff));	\
-    } while (0)
+static inline void
+txdr_hyper(uint64_t f, uint32_t* t)
+{
+	t[0] = htonl((u_int32_t)(f >> 32));
+	t[1] = htonl((u_int32_t)(f & 0xffffffff));
+}
 
 #endif	/* _NFS_XDR_SUBS_H_ */
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index f06c2d5a6975..cce291a8f1f6 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -2258,10 +2258,11 @@ again:
 			(void) nfsm_strtom(nd, dp->d_name, nlen);
 			if (nd->nd_flag & ND_NFSV3) {
 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
-				*tl++ = 0;
-			} else
+				txdr_hyper(*cookiep, tl);
+			} else {
 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
-			*tl = txdr_unsigned(*cookiep);
+				*tl = txdr_unsigned(*cookiep);
+			}
 		}
 		cpos += dp->d_reclen;
 		dp = (struct dirent *)cpos;
@@ -2747,8 +2748,7 @@ again:
 				*tl = txdr_unsigned(dp->d_fileno);
 				dirlen += nfsm_strtom(nd, dp->d_name, nlen);
 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
-				*tl++ = 0;
-				*tl = txdr_unsigned(*cookiep);
+				txdr_hyper(*cookiep, tl);
 				nfsrv_postopattr(nd, 0, nvap);
 				dirlen += nfsm_fhtom(nd,(u_int8_t *)&nfh,0,1);
 				dirlen += (5*NFSX_UNSIGNED+NFSX_V3POSTOPATTR);
@@ -2757,8 +2757,7 @@ again:
 			} else {
 				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
 				*tl++ = newnfs_true;
-				*tl++ = 0;
-				*tl = txdr_unsigned(*cookiep);
+				txdr_hyper(*cookiep, tl);
 				dirlen += nfsm_strtom(nd, dp->d_name, nlen);
 				if (nvp != NULL) {
 					supports_nfsv4acls =
diff --git a/sys/fs/nfsserver/nfs_nfsdsubs.c b/sys/fs/nfsserver/nfs_nfsdsubs.c
index 8c3e748a290f..8dc193a314e5 100644
--- a/sys/fs/nfsserver/nfs_nfsdsubs.c
+++ b/sys/fs/nfsserver/nfs_nfsdsubs.c
@@ -1433,13 +1433,13 @@ nfsrv_fillattr(struct nfsrv_descript *nd, struct nfsvattr *nvap)
 	if (nd->nd_flag & ND_NFSV3) {
 		fp->fa_type = vtonfsv34_type(nvap->na_type);
 		fp->fa_mode = vtonfsv34_mode(nvap->na_mode);
-		txdr_hyper(nvap->na_size, &fp->fa3_size);
-		txdr_hyper(nvap->na_bytes, &fp->fa3_used);
+		txdr_hyper(nvap->na_size, (uint32_t*)&fp->fa3_size);
+		txdr_hyper(nvap->na_bytes, (uint32_t*)&fp->fa3_used);
 		fp->fa3_rdev.specdata1 = txdr_unsigned(NFSMAJOR(nvap->na_rdev));
 		fp->fa3_rdev.specdata2 = txdr_unsigned(NFSMINOR(nvap->na_rdev));
 		fp->fa3_fsid.nfsuquad[0] = 0;
 		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(nvap->na_fsid);
-		txdr_hyper(nvap->na_fileid, &fp->fa3_fileid);
+		txdr_hyper(nvap->na_fileid, (uint32_t*)&fp->fa3_fileid);
 		txdr_nfsv3time(&nvap->na_atime, &fp->fa3_atime);
 		txdr_nfsv3time(&nvap->na_mtime, &fp->fa3_mtime);
 		txdr_nfsv3time(&nvap->na_ctime, &fp->fa3_ctime);
diff --git a/sys/nfs/xdr_subs.h b/sys/nfs/xdr_subs.h
index a4f7ae0b7e40..84155035c1cd 100644
--- a/sys/nfs/xdr_subs.h
+++ b/sys/nfs/xdr_subs.h
@@ -83,10 +83,12 @@ do { \
 #define	fxdr_hyper(f) \
 	((((u_quad_t)ntohl(((u_int32_t *)(f))[0])) << 32) | \
 	 (u_quad_t)(ntohl(((u_int32_t *)(f))[1])))
-#define	txdr_hyper(f, t) \
-do { \
-	((u_int32_t *)(t))[0] = htonl((u_int32_t)((f) >> 32)); \
-	((u_int32_t *)(t))[1] = htonl((u_int32_t)((f) & 0xffffffff)); \
-} while (0)
+
+static inline void
+txdr_hyper(uint64_t f, uint32_t* t)
+{
+	t[0] = htonl((u_int32_t)(f >> 32));
+	t[1] = htonl((u_int32_t)(f & 0xffffffff));
+}
 
 #endif