diff --git a/sys/vfs/nfs/nfs_bio.c b/sys/vfs/nfs/nfs_bio.c --- a/sys/vfs/nfs/nfs_bio.c +++ b/sys/vfs/nfs/nfs_bio.c @@ -256,8 +256,24 @@ n = biosize - boff; if (n > uio->uio_resid) n = uio->uio_resid; - if (loffset + boff + n > np->n_size) - n = np->n_size - loffset - boff; + /* + * Re-read np->n_size into a local. A concurrent + * ftruncate()/stat() may have called nfs_meta_setsize() + * to reduce np->n_size while nfs_doio() above slept in + * the READ RPC. Guard the unsigned subtraction against + * underflow when n_size < loffset + boff, which would + * cause uiomovebp() to copy far past the biosize buffer + * into adjacent kernel memory (TOCTOU / OOB heap read). + */ + { + u_quad_t cur_size = np->n_size; + if (loffset + boff + n > cur_size) { + if (cur_size > (u_quad_t)(loffset + boff)) + n = (size_t)(cur_size - loffset - boff); + else + n = 0; + } + } break; case VLNK: biosize = min(NFS_MAXPATHLEN, np->n_size);