DF-2993 / fix_all_three.diff
--- nfs_orig.c 2026-09-04 20:56:50.067542992 +0000 +++ nfs_new.c 2026-09-04 20:56:50.083542794 +0000 @@ -586,6 +586,15 @@ error = cache_vget(&nd.nl_nch, nd.nl_cred, LK_EXCLUSIVE, &vp); KKASSERT(error == 0); + } else { + /* + * DF-2994: the index lookup failed, so we + * continue with the original vp — which was + * unlocked above. Restore its lock so the + * vput() paths below cannot release an + * unheld lock (guaranteed panic). + */ + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); } error = 0; } @@ -1307,11 +1316,19 @@ nfsmout: m_freem(info.mrep); info.mrep = NULL; - error = EIO; - nfsm_writereply(&info, nfsd, slp, error, 2 * NFSX_UNSIGNED); - if (info.v3) { - nfsm_srvwcc_data(&info, nfsd, forat_ret, &forat, - aftat_ret, &va); + /* + * DF-2995: when we got here through NEGREPLYOUT()'s -2 path + * nfsm_reply() already built a reply into info.mreq — do + * not build (and leak) a second one. + */ + if (info.mreq == NULL) { + error = EIO; + nfsm_writereply(&info, nfsd, slp, error, + 2 * NFSX_UNSIGNED); + if (info.v3) { + nfsm_srvwcc_data(&info, nfsd, forat_ret, &forat, + aftat_ret, &va); + } } nfsd->nd_mreq = info.mreq; nfsd->nd_mrep = NULL; @@ -3009,6 +3026,14 @@ if (!error && toff && verf && verf != at.va_filerev) error = NFSERR_BAD_COOKIE; #endif + /* + * DF-2993: a count of 0 from the wire makes siz/fullsiz 0 + * which turns the degenerate-case retry loop below into an + * infinite loop (no eof reply is possible because that path + * requires uio_resid > 0). + */ + if (siz <= 0) + error = NFSERR_TOOSMALL; } if (!error) error = nfsrv_access(mp, vp, VEXEC, cred, rdonly, td, 0); @@ -3305,6 +3330,12 @@ if (!error && toff && verf && verf != at.va_filerev) error = NFSERR_BAD_COOKIE; #endif + /* + * DF-2993: a dircount of 0 from the wire makes siz/fullsiz 0 which + * turns the degenerate-case retry loop below into an infinite loop. + */ + if (siz <= 0) + error = NFSERR_TOOSMALL; if (!error) { error = nfsrv_access(mp, vp, VEXEC, cred, rdonly, td, 0); } |