git: 968abc191ea1 - stable/13 - nfscl: Fix a crash when a readdir entry has nul in it

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Fri, 24 Jan 2025 02:31:21 UTC
The branch stable/13 has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=968abc191ea145040f4c2105755d11b63ecb2427

commit 968abc191ea145040f4c2105755d11b63ecb2427
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2025-01-10 03:54:41 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2025-01-24 02:30:22 +0000

    nfscl: Fix a crash when a readdir entry has nul in it
    
    Commit 026cdaa3b3a9 added a check for a nul or "/" in a file
    name in a readdir reply.  Unfortunately, the minimal testing
    done on it did not detect a bug that can cause the client
    to crash.
    
    This patch fixes the code so that it does not crash.
    
    Note that a NFS server will not normally return a file
    name in a readdir reply that has a nul or "/" in it,
    so the crash is unlikely.
    
    PR:     283965
    
    (cherry picked from commit f9f0a1d61c7b97c705246c747baec385e0592966)
---
 sys/fs/nfsclient/nfs_clrpcops.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index b3062b5a5d88..f3ab2bda8f85 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -3079,6 +3079,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 	nfsattrbit_t attrbits, dattrbits;
 	u_int32_t rderr, *tl2 = NULL;
 	size_t tresid;
+	bool validentry;
 
 	KASSERT(uiop->uio_iovcnt == 1 &&
 	    (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0,
@@ -3305,6 +3306,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 
 		/* loop through the dir entries, doctoring them to 4bsd form */
 		while (more_dirs && bigenough) {
+			validentry = true;
 			if (nd->nd_flag & ND_NFSV4) {
 				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
 				ncookie.lval[0] = *tl++;
@@ -3384,6 +3386,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 					uiop->uio_offset = savoff;
 					uiop->uio_resid = savresid;
 					blksiz = savblksiz;
+					validentry = false;
 				} else {
 					cp = uiop->uio_iov->iov_base;
 					tlen -= len;
@@ -3421,7 +3424,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 				ncookie.lval[0] = 0;
 				ncookie.lval[1] = *tl++;
 			}
-			if (bigenough) {
+			if (bigenough && validentry) {
 			    if (nd->nd_flag & ND_NFSV4) {
 				if (rderr) {
 				    dp->d_fileno = 0;
@@ -3560,7 +3563,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 	size_t tresid;
 	u_int32_t *tl2 = NULL, rderr;
 	struct timespec dctime, ts;
-	bool attr_ok;
+	bool attr_ok, validentry;
 
 	KASSERT(uiop->uio_iovcnt == 1 &&
 	    (uiop->uio_resid & (DIRBLKSIZ - 1)) == 0,
@@ -3771,6 +3774,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 
 		/* loop through the dir entries, doctoring them to 4bsd form */
 		while (more_dirs && bigenough) {
+			validentry = true;
 			NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
 			if (nd->nd_flag & ND_NFSV4) {
 				ncookie.lval[0] = *tl++;
@@ -3846,6 +3850,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 					uiop->uio_offset = savoff;
 					uiop->uio_resid = savresid;
 					blksiz = savblksiz;
+					validentry = false;
 				} else {
 					cp = uiop->uio_iov->iov_base;
 					tlen -= len;
@@ -3902,7 +3907,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 					goto nfsmout;
 			}
 
-			if (bigenough) {
+			if (bigenough && validentry) {
 			    if (nd->nd_flag & ND_NFSV4) {
 				if (rderr) {
 				    dp->d_fileno = 0;