DF-3106 / fix.diff
--- a/sys/vfs/ufs/ufs_lookup.c +++ b/sys/vfs/ufs/ufs_lookup.c @@ -808,6 +808,26 @@ for (loc = ep->d_reclen; loc < dp->i_count; ) { nep = (struct direct *)(dirbuf + loc); + /* + * The compaction region was validated by ufs_lookup() for + * forward progress only (d_reclen != 0 and fitting in the + * remaining DIRBLKSIZ space); with debug.dircheck == 0 the + * d_namlen was never checked, so DIRSIZ(nep) can exceed + * nep->d_reclen on a corrupted filesystem image. Compacting + * such an entry copies DIRSIZ(nep) bytes and advances the + * destination pointer by that amount each iteration, walking + * far past the end of the directory buffer (kernel heap OOB + * write, DF-3106). Refuse corrupted regions instead. + */ + if (nep->d_reclen == 0 || + nep->d_reclen > dp->i_count - loc || + (nep->d_ino != 0 && + nep->d_reclen < DIRSIZ(OFSFMT(dvp), nep))) { + bp->b_flags |= B_INVAL; + brelse(bp); + return (EIO); + } + /* Trim the existing slot (NB: dsize may be zero). */ ep->d_reclen = dsize; ep = (struct direct *)((char *)ep + dsize); |