From 8cfd5285c09bbf2ddabe009f013191981c56fc85 Mon Sep 17 00:00:00 2001 From: millert Date: Mon, 4 May 2026 17:05:59 +0000 Subject: [PATCH] Add checks for invalid dir count and max size for readdir/readdirplus. A zero count or max size value is now rejected early instead of relying on VOP_GETATTR to return an error. Also verify that the max size after rounding up to a multiple of DIRBLKSIZ is positive. A negative value would turn into a large allocation, causing the malloc() to fail. From an LLM bug report. With help from miod@ and kirill@. --- sys/nfs/nfs_serv.c | 57 +++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/sys/nfs/nfs_serv.c b/sys/nfs/nfs_serv.c index 63befba0cd1..58caaf02ccb 100644 --- a/sys/nfs/nfs_serv.c +++ b/sys/nfs/nfs_serv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nfs_serv.c,v 1.132 2025/04/30 18:38:20 miod Exp $ */ +/* $OpenBSD: nfs_serv.c,v 1.133 2026/05/04 17:05:59 millert Exp $ */ /* $NetBSD: nfs_serv.c,v 1.34 1997/05/12 23:37:12 fvdl Exp $ */ /* @@ -2427,17 +2427,24 @@ nfsrv_readdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, } off = toff; cnt = fxdr_unsigned(int, *tl); - xfer = NFS_SRVMAXDATA(nfsd); - if (cnt > xfer || cnt < 0) - cnt = xfer; - siz = ((cnt + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); - if (siz > xfer) - siz = xfer; - fullsiz = siz; - error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly); - if (!error && vp->v_type != VDIR) { - error = ENOTDIR; - vput(vp); + if (cnt == 0) { + if (info.nmi_v3) + error = NFSERR_TOOSMALL; + else + error = EBADRPC; + } else { + xfer = NFS_SRVMAXDATA(nfsd); + if (cnt > xfer || cnt < 0) + cnt = xfer; + siz = ((cnt + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); + if (siz > xfer || siz <= 0) + siz = xfer; + fullsiz = siz; + error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly); + if (!error && vp->v_type != VDIR) { + error = ENOTDIR; + vput(vp); + } } if (error) { if (nfsm_reply(&info, nfsd, slp, mrq, error, @@ -2649,17 +2656,21 @@ nfsrv_readdirplus(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, tl += 2; siz = fxdr_unsigned(int, *tl++); cnt = fxdr_unsigned(int, *tl); - xfer = NFS_SRVMAXDATA(nfsd); - if (cnt > xfer || cnt < 0) - cnt = xfer; - siz = ((siz + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); - if (siz > xfer) - siz = xfer; - fullsiz = siz; - error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly); - if (!error && vp->v_type != VDIR) { - error = ENOTDIR; - vput(vp); + if (siz == 0 || cnt == 0) { + error = NFSERR_TOOSMALL; + } else { + xfer = NFS_SRVMAXDATA(nfsd); + if (cnt > xfer || cnt < 0) + cnt = xfer; + siz = ((siz + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); + if (siz > xfer || siz <= 0) + siz = xfer; + fullsiz = siz; + error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly); + if (!error && vp->v_type != VDIR) { + error = ENOTDIR; + vput(vp); + } } if (error) { if (nfsm_reply(&info, nfsd, slp, mrq, error,