DF-0778 / fix.diff
diff --git a/sys/vfs/ufs/ufs_vnops.c b/sys/vfs/ufs/ufs_vnops.c --- a/sys/vfs/ufs/ufs_vnops.c +++ b/sys/vfs/ufs/ufs_vnops.c @@ -1737,12 +1737,22 @@ { struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); - int isize; + int64_t isize; /* was: int -- ip->i_size is uint64_t */ isize = ip->i_size; - if ((isize < vp->v_mount->mnt_maxsymlinklen) || - (ip->i_din.di_blocks == 0)) { /* XXX - for old fastlink support */ - uiomove((char *)ip->i_shortlink, isize, ap->a_uio); + /* + * Only trust the inline i_shortlink buffer when isize fits within it + * (i_shortlink overlays di_db, UFS1_MAXSYMLINKLEN bytes). A crafted + * or corrupted inode with di_blocks==0 but a huge or negative i_size + * must NOT be passed to uiomove() with the inline buffer: previously + * i_size was truncated to a signed int and then sign-extended to a + * size_t, causing either an out-of-bounds read past the inode + * (kernel heap info leak) or a vm_fault panic (DF-0778). + */ + if (isize >= 0 && isize <= UFS1_MAXSYMLINKLEN && + ((isize < vp->v_mount->mnt_maxsymlinklen) || + (ip->i_din.di_blocks == 0))) { /* XXX - for old fastlink support */ + uiomove((char *)ip->i_shortlink, (size_t)isize, ap->a_uio); return (0); } |