DF-2998 / fix.diff
--- a/sys/vfs/nfs/nfs_subs.c +++ b/sys/vfs/nfs/nfs_subs.c @@ -718,19 +718,39 @@ * device, and if so, check for a possible alias. Once the * correct vnode has been obtained, fill in the rest of the * information. + * + * DF-2998: retyping a LIVE (already-typed) vnode from server + * attributes is not legitimate -- a file handle identifies an + * object whose type does not change over its lifetime. A + * malicious server used this to flip v_type/v_ops mid-life + * (VREG->VDIR, VREG->VCHR with server rdev, VDIR->VREG), + * corrupting type-dependent state: nfs_inactive()'s sillyrename + * branch reads n_un3.nf_silly which aliases the VDIR cookie + * list head (kernel panic in nfs_removerpc), and the + * VDIR->VREG case feeds directory buffer state to nvtruncbuf + * (bgetvp "overlapping buffer" panic). Only assign the type + * at creation (VNON); if a live vnode's type differs, refuse + * the attributes, invalidate the cache, and fail the load so + * callers treat the reply as bogus. */ np = VTONFS(vp); if (vp->v_type != vtyp) { - nfs_setvtype(vp, vtyp); - if (vp->v_type == VFIFO) { - vp->v_ops = &vp->v_mount->mnt_vn_fifo_ops; - } else if (vp->v_type == VCHR || vp->v_type == VBLK) { - vp->v_ops = &vp->v_mount->mnt_vn_spec_ops; - addaliasu(vp, rmajor, rminor); + if (vp->v_type == VNON) { + nfs_setvtype(vp, vtyp); + if (vp->v_type == VFIFO) { + vp->v_ops = &vp->v_mount->mnt_vn_fifo_ops; + } else if (vp->v_type == VCHR || + vp->v_type == VBLK) { + vp->v_ops = &vp->v_mount->mnt_vn_spec_ops; + addaliasu(vp, rmajor, rminor); + } else { + vp->v_ops = &vp->v_mount->mnt_vn_use_ops; + } + np->n_mtime = mtime.tv_sec; } else { - vp->v_ops = &vp->v_mount->mnt_vn_use_ops; + np->n_attrstamp = 0; + return (EBADRPC); } - np->n_mtime = mtime.tv_sec; } else if (np->n_mtime != mtime.tv_sec) { /* * If we haven't modified the file locally and the server |