DragonFlyBSD Kernel Audit
DF-0857 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/hpfs/hpfs_alsubr.c b/sys/vfs/hpfs/hpfs_alsubr.c
index 8f1722d..7ffdbe9 100644
--- a/sys/vfs/hpfs/hpfs_alsubr.c
+++ b/sys/vfs/hpfs/hpfs_alsubr.c
@@ -57,6 +57,24 @@
 				  alnode_t *);
 
 /*
+ * Maximum legitimate ab_busycnt for the in-kernel containers, computed from
+ * the on-disk data-area sizes (sys/vfs/hpfs/hpfs_alsubr.c includes hpfs.h):
+ *   fnode fn_abd[0x60]  = 96 bytes
+ *   alsec as_abd[0x1E0] = 480 bytes
+ * alleaf_t = 12 bytes, alnode_t = 8 bytes (hpfs.h).
+ *
+ * ab_busycnt is taken straight from the on-disk alblk_t and is untrusted;
+ * these caps prevent the iteration loops in hpfs_hpbmap from walking past
+ * the data area into neighbouring heap memory (DF-0857).
+ */
+#define	HPFS_FN_ABD_SIZE	0x60
+#define	HPFS_AS_ABD_SIZE	0x1E0
+#define	HPFS_FN_MAX_LEAF	(HPFS_FN_ABD_SIZE / sizeof(alleaf_t))	/*  8 */
+#define	HPFS_FN_MAX_NODE	(HPFS_FN_ABD_SIZE / sizeof(alnode_t))	/* 12 */
+#define	HPFS_AS_MAX_LEAF	(HPFS_AS_ABD_SIZE / sizeof(alleaf_t))	/* 40 */
+#define	HPFS_AS_MAX_NODE	(HPFS_AS_ABD_SIZE / sizeof(alnode_t))	/* 60 */
+
+/*
  * Map file offset to disk offset. hpfsnode have to be locked.
  */
 int
@@ -66,7 +84,7 @@
 	alblk_t * abp;
 	alleaf_t *alp;
 	alnode_t *anp;
-	int error, i;
+	int error, i, in_fnode;
 
 	dprintf(("hpfs_hpbmap(0x%x, 0x%x): ",hp->h_no, bn));
 
@@ -74,8 +92,21 @@
 	abp = &hp->h_fn.fn_ab;
 	alp = (alleaf_t *)&hp->h_fn.fn_abd;
 	anp = (alnode_t *)&hp->h_fn.fn_abd;
+	in_fnode = 1;
 
 dive:
+	{
+		u_int maxcnt = (abp->ab_flag & AB_NODES) ?
+		    (in_fnode ? HPFS_FN_MAX_NODE : HPFS_AS_MAX_NODE) :
+		    (in_fnode ? HPFS_FN_MAX_LEAF : HPFS_AS_MAX_LEAF);
+		if (abp->ab_busycnt > maxcnt) {
+			kprintf("hpfs_hpbmap: forged ab_busycnt %u > max %u\n",
+			    abp->ab_busycnt, maxcnt);
+			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));
@@ -106,6 +137,7 @@
 				abp = &asp->as_ab;
 				alp = (alleaf_t *)&asp->as_abd;
 				anp = (alnode_t *)&asp->as_abd;
+				in_fnode = 0;
 
 				goto dive;
 			}