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; iab_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;