DF-0858 / fix.diff
diff --git a/sys/vfs/hpfs/hpfs_alsubr.c b/sys/vfs/hpfs/hpfs_alsubr.c index 0000000..1111111 100644 --- a/sys/vfs/hpfs/hpfs_alsubr.c +++ b/sys/vfs/hpfs/hpfs_alsubr.c @@ -57,6 +57,16 @@ alnode_t *); /* + * Maximum legitimate AlSec dive depth in hpfs_hpbmap's allocation tree. + * Each AlSec node-fanout holds up to 60 AlNodes (as_abd[0x1E0]/sizeof(alnode_t)) + * and each fnode-root holds up to 12 (fn_abd[0x60]/sizeof(alnode_t)), so even a + * maximally-fragmented volume reaches only a handful of levels. A dive deeper + * than this is a corrupt or cyclic image and MUST be rejected to avoid an + * unkillable kernel CPU spin (DF-0858: cyclic AlSec pointers A<->B). + */ +#define HPFS_DIVE_MAX 20 + +/* * Map file offset to disk offset. hpfsnode have to be locked. */ int @@ -66,7 +76,7 @@ alblk_t * abp; alleaf_t *alp; alnode_t *anp; - int error, i; + int error, i, depth; dprintf(("hpfs_hpbmap(0x%x, 0x%x): ",hp->h_no, bn)); @@ -74,8 +84,16 @@ abp = &hp->h_fn.fn_ab; alp = (alleaf_t *)&hp->h_fn.fn_abd; anp = (alnode_t *)&hp->h_fn.fn_abd; + depth = 0; dive: + if (depth++ > HPFS_DIVE_MAX) { + kprintf("hpfs_hpbmap: dive depth %d exceeds %d " + "(cyclic or corrupt AlSec tree)\n", depth, HPFS_DIVE_MAX); + if (bp) + brelse(bp); + return (EINVAL); + } if (abp->ab_flag & AB_NODES) { for (i=0; i<abp->ab_busycnt; i++, anp++) { dprintf(("[0x%x,0x%x] ",anp->an_nextoff,anp->an_lsn)); |