git: dc37121d3210 - main - ffs_reallocblks(): ensure that pref cg is valid

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Mon, 13 Jan 2025 19:23:31 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=dc37121d3210d08c96a883ebfed780660e7e2b39

commit dc37121d3210d08c96a883ebfed780660e7e2b39
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2025-01-05 22:51:23 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2025-01-13 19:22:54 +0000

    ffs_reallocblks(): ensure that pref cg is valid
    
    ffs_blkpref_ufsX() must return in-range pref frag number, otherwise
    calculated cg index is out of range for fs, causing out of range
    accesses to the structures sized by the number of cg, e.g. the
    fs_maxcluster[] array in ffs_clusteralloc().
    
    The easiest way to trigger it is to overflow the volume.
    
    In collaboration with:  pho
    Reviewed by:    mckusick
    Sponsored by:   The FreeBSD Foundation
    MFC afer:       1 week
    Differential revision:  https://reviews.freebsd.org/D48378
---
 sys/ufs/ffs/ffs_alloc.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
index 01bfdb85c2e6..265daef14812 100644
--- a/sys/ufs/ffs/ffs_alloc.c
+++ b/sys/ufs/ffs/ffs_alloc.c
@@ -681,6 +681,7 @@ ffs_reallocblks_ufs1(
 	 * groups that we will search.
 	 */
 	cg = dtog(fs, pref);
+	MPASS(cg < fs->fs_ncg);
 	for (i = min(maxclustersearch, fs->fs_ncg); i > 0; i--) {
 		if ((newblk = ffs_clusteralloc(ip, cg, pref, len)) != 0)
 			break;
@@ -947,6 +948,7 @@ ffs_reallocblks_ufs2(
 	 * groups that we will search.
 	 */
 	cg = dtog(fs, pref);
+	MPASS(cg < fs->fs_ncg);
 	for (i = min(maxclustersearch, fs->fs_ncg); i > 0; i--) {
 		if ((newblk = ffs_clusteralloc(ip, cg, pref, len)) != 0)
 			break;
@@ -1438,8 +1440,11 @@ ffs_blkpref_ufs1(struct inode *ip,
 		 * place it immediately following the last direct block.
 		 */
 		if (indx == -1 && lbn < UFS_NDADDR + NINDIR(fs) &&
-		    ip->i_din1->di_db[UFS_NDADDR - 1] != 0)
+		    ip->i_din1->di_db[UFS_NDADDR - 1] != 0) {
 			pref = ip->i_din1->di_db[UFS_NDADDR - 1] + fs->fs_frag;
+			if (dtog(fs, pref) >= fs->fs_ncg)
+				pref = 0;
+		}
 		return (pref);
 	}
 	/*
@@ -1450,8 +1455,11 @@ ffs_blkpref_ufs1(struct inode *ip,
 	if (lbn == UFS_NDADDR) {
 		pref = ip->i_din1->di_ib[0];
 		if (pref != 0 && pref >= cgdata(fs, inocg) &&
-		    pref < cgbase(fs, inocg + 1))
+		    pref < cgbase(fs, inocg + 1)) {
+			if (dtog(fs, pref + fs->fs_frag) >= fs->fs_ncg)
+				return (0);
 			return (pref + fs->fs_frag);
+		}
 	}
 	/*
 	 * If we are at the beginning of a file, or we have already allocated
@@ -1506,6 +1514,8 @@ ffs_blkpref_ufs1(struct inode *ip,
 	/*
 	 * Otherwise, we just always try to lay things out contiguously.
 	 */
+	if (dtog(fs, prevbn + fs->fs_frag) >= fs->fs_ncg)
+		return (0);
 	return (prevbn + fs->fs_frag);
 }
 
@@ -1550,8 +1560,11 @@ ffs_blkpref_ufs2(struct inode *ip,
 		 * place it immediately following the last direct block.
 		 */
 		if (indx == -1 && lbn < UFS_NDADDR + NINDIR(fs) &&
-		    ip->i_din2->di_db[UFS_NDADDR - 1] != 0)
+		    ip->i_din2->di_db[UFS_NDADDR - 1] != 0) {
 			pref = ip->i_din2->di_db[UFS_NDADDR - 1] + fs->fs_frag;
+			if (dtog(fs, pref) >= fs->fs_ncg)
+				pref = 0;
+		}
 		return (pref);
 	}
 	/*
@@ -1562,8 +1575,11 @@ ffs_blkpref_ufs2(struct inode *ip,
 	if (lbn == UFS_NDADDR) {
 		pref = ip->i_din2->di_ib[0];
 		if (pref != 0 && pref >= cgdata(fs, inocg) &&
-		    pref < cgbase(fs, inocg + 1))
+		    pref < cgbase(fs, inocg + 1)) {
+			if (dtog(fs, pref + fs->fs_frag) >= fs->fs_ncg)
+				return (0);
 			return (pref + fs->fs_frag);
+		}
 	}
 	/*
 	 * If we are at the beginning of a file, or we have already allocated
@@ -1618,6 +1634,8 @@ ffs_blkpref_ufs2(struct inode *ip,
 	/*
 	 * Otherwise, we just always try to lay things out contiguously.
 	 */
+	if (dtog(fs, prevbn + fs->fs_frag) >= fs->fs_ncg)
+		return (0);
 	return (prevbn + fs->fs_frag);
 }
 
@@ -1968,6 +1986,7 @@ ffs_clusteralloc(struct inode *ip,
 
 	ump = ITOUMP(ip);
 	fs = ump->um_fs;
+	MPASS(cg < fs->fs_ncg);
 	if (fs->fs_maxcluster[cg] < len)
 		return (0);
 	UFS_UNLOCK(ump);