DragonFlyBSD Kernel Audit
DF-0858 / fix.diff
← back to finding ↓ download raw
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));