⬢ DragonFlyBSD Kernel Audit
DF-3140 / fix.diff
← back to finding ↓ download raw
diff -ru a/sys/vfs/hpfs/hpfs_alsubr.c b/sys/vfs/hpfs/hpfs_alsubr.c
--- a/sys/vfs/hpfs/hpfs_alsubr.c	2026-09-07 00:07:33.946952490 +0000
+++ b/sys/vfs/hpfs/hpfs_alsubr.c	2026-09-07 00:15:22.128941247 +0000
@@ -96,12 +96,17 @@
 				}
 
 				asp = (alsec_t *) bp->b_data;
-				if (asp->as_magic != AS_MAGIC) {
-					brelse(bp);
-					kprintf("hpfs_hpbmap: "
-					       "MAGIC DOESN'T MATCH");
-					return (EINVAL);
-				}
+			if (asp->as_magic != AS_MAGIC) {
+				brelse(bp);
+				kprintf("hpfs_hpbmap: "
+				       "MAGIC DOESN'T MATCH");
+				return (EINVAL);
+			}
+			if (!hpfs_alblk_sane(&asp->as_ab, 0x1E0)) {
+				brelse(bp);
+				kprintf("hpfs_hpbmap: INSANE ALBLK\n");
+				return (EINVAL);
+			}
 
 				abp = &asp->as_ab;
 				alp = (alleaf_t *)&asp->as_abd;
@@ -115,8 +120,8 @@
 			dprintf(("[0x%x,0x%x,0x%x] ",
 				 alp->al_off,alp->al_len,alp->al_lsn));
 
-			if ((bn >= alp->al_off) &&
-			    (!alp->al_len || (bn < alp->al_off + alp->al_len))) {
+			if ((bn >= alp->al_off) && alp->al_len &&
+			    (bn < alp->al_off + alp->al_len)) {
 				dprintf(("found, "));
 
 				*bnp = bn - alp->al_off + alp->al_lsn;
@@ -496,8 +501,10 @@
 			anp = AB_ALNODE(rabp);
 			for (i=0; i<rabp->ab_busycnt; i++) {
 				error = hpfs_breadalsec(hpmp, anp->an_lsn, &bp);
-				if (error)
+				if (error) {
+					brelse(nbp);
 					return (error);
+				}
 
 				asp = (alsec_t *)bp->b_data;
 				asp->as_ab.ab_flag &= ~AB_FNPARENT;
@@ -562,6 +569,12 @@
 	if (error)
 		return (error);
 
+	if (!hpfs_alblk_sane(&((alsec_t *)rbp->b_data)->as_ab, 0x1E0)) {
+		brelse(rbp);
+		kprintf("hpfs_addextentr: INSANE ALBLK\n");
+		return (EINVAL);
+	}
+
 	rasp = (alsec_t *)rbp->b_data;
 	rabp = &rasp->as_ab;
 	wb = 0;
@@ -767,6 +780,12 @@
 
 			asp = (alsec_t *)bp->b_data;
 
+			if (!hpfs_alblk_sane(&asp->as_ab, 0x1E0)) {
+				brelse(bp);
+				kprintf("hpfs_truncatealblk: INSANE ALBLK\n");
+				return (EINVAL);
+			}
+
 			error = hpfs_truncatealblk (hpmp,
 					&asp->as_ab, bn, resp);
 			if (error) {
@@ -795,10 +814,12 @@
 					alsec_t *as0p;
 					struct buf *b0p;
 
-					error = hpfs_breadalsec(hpmp,
-							(anp-1)->an_lsn, &b0p);
-					if (error)
-						return (error);
+				error = hpfs_breadalsec(hpmp,
+						(anp-1)->an_lsn, &b0p);
+				if (error) {
+					brelse(bp);
+					return (error);
+				}
 
 					as0p = (alsec_t *)b0p->b_data;
 					error = hpfs_concatalsec(hpmp,
diff -ru a/sys/vfs/hpfs/hpfs_subr.c b/sys/vfs/hpfs/hpfs_subr.c
--- a/sys/vfs/hpfs/hpfs_subr.c	2026-09-07 00:07:33.946952490 +0000
+++ b/sys/vfs/hpfs/hpfs_subr.c	2026-09-07 00:07:33.946952490 +0000
@@ -866,3 +866,27 @@
 	return (0);
 }
 
+
+/*
+ * Validate an on-disk AlBlk header before its busycnt/freecnt/freeoff
+ * are used as array bounds/offsets.  HPFS AlBlks are packed arrays of
+ * alleaf_t (or alnode_t) starting at sizeof(alblk_t); the kernel itself
+ * maintains exactly this invariant (AB_ADDNREC/AB_RMNREC/split/concat).
+ * abdsize is the byte size of the entry area (0x60 for an FNode root,
+ * 0x1E0 for an AlSec).
+ */
+int
+hpfs_alblk_sane (alblk_t *abp, int abdsize)
+{
+	int sz = (abp->ab_flag & AB_NODES) ?
+			sizeof(alnode_t) : sizeof(alleaf_t);
+	int cap = abdsize / sz;
+
+	if (abp->ab_busycnt > cap)
+		return (0);
+	if (abp->ab_freeoff != sizeof(alblk_t) + abp->ab_busycnt * sz)
+		return (0);
+	if (abp->ab_freecnt > cap - abp->ab_busycnt)
+		return (0);
+	return (1);
+}
diff -ru a/sys/vfs/hpfs/hpfs_subr.h b/sys/vfs/hpfs/hpfs_subr.h
--- a/sys/vfs/hpfs/hpfs_subr.h	2026-09-07 00:07:33.946952490 +0000
+++ b/sys/vfs/hpfs/hpfs_subr.h	2026-09-07 00:07:33.946952490 +0000
@@ -36,6 +36,7 @@
 void		hpfs_bmdeinit (struct hpfsmount *);
 int		hpfs_bmfblookup (struct hpfsmount *, lsn_t *);
 int		hpfs_bmmark (struct hpfsmount *, lsn_t, u_long, int);
+int		hpfs_alblk_sane (alblk_t *, int);
 int		hpfs_bmlookup (struct hpfsmount *, u_long, lsn_t, u_long,
 				lsn_t *, u_long *);
 
diff -ru a/sys/vfs/hpfs/hpfs_vfsops.c b/sys/vfs/hpfs/hpfs_vfsops.c
--- a/sys/vfs/hpfs/hpfs_vfsops.c	2026-09-07 00:07:33.946952490 +0000
+++ b/sys/vfs/hpfs/hpfs_vfsops.c	2026-09-07 00:07:33.946952490 +0000
@@ -540,6 +540,11 @@
 		vx_put(vp);
 		return (EINVAL);
 	}
+	if (!hpfs_alblk_sane(&hp->h_fn.fn_ab, 0x60)) {
+		kprintf("hpfs_vget: INSANE ALBLK at 0x%x\n", (unsigned)ino);
+		vx_put(vp);
+		return (EINVAL);
+	}
 
 	vp->v_type = hp->h_fn.fn_flag ? VDIR:VREG;
 	hp->h_flag &= ~H_INVAL;