git: 3035f98d56eb - stable/13 - Fix a bug in fsck_ffs(8) triggered by corrupted filesystems.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Wed, 07 Jun 2023 23:15:36 UTC
The branch stable/13 has been updated by mckusick:

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

commit 3035f98d56eb72240c6260d667427ab4ade08b45
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2023-05-28 00:09:02 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2023-06-07 22:46:53 +0000

    Fix a bug in fsck_ffs(8) triggered by corrupted filesystems.
    
    Reported-by:  Robert Morris
    PR:           271378
    Sponsored-by: The FreeBSD Foundation
    
    (cherry picked from commit 101a9ac07128a17d8797cc3e93978d2cfa457e99)
---
 sbin/fsck_ffs/fsck.h   |  1 +
 sbin/fsck_ffs/fsutil.c | 25 +++++++++++++++++++++++++
 sbin/fsck_ffs/pass1.c  | 12 +-----------
 sbin/fsck_ffs/suj.c    |  3 +++
 4 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
index 9f9b1e0a8857..4f77fc39f3f7 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -471,6 +471,7 @@ void		check_blkcnt(struct inode *ip);
 int		check_cgmagic(int cg, struct bufarea *cgbp);
 void		rebuild_cg(int cg, struct bufarea *cgbp);
 void		check_dirdepth(struct inoinfo *inp);
+int		chkfilesize(mode_t mode, u_int64_t filesize);
 int		chkrange(ufs2_daddr_t blk, int cnt);
 void		ckfini(int markclean);
 int		ckinode(union dinode *dp, struct inodesc *);
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
index 419d0e91a1df..ab55bdfbb37a 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -1206,6 +1206,31 @@ std_checkblkavail(ufs2_daddr_t blkno, long frags)
 	return (0);
 }
 
+/*
+ * Check whether a file size is within the limits for the filesystem.
+ * Return 1 when valid and 0 when too big.
+ *
+ * This should match the file size limit in ffs_mountfs().
+ */
+int
+chkfilesize(mode_t mode, u_int64_t filesize)
+{
+	u_int64_t kernmaxfilesize;
+
+	if (sblock.fs_magic == FS_UFS1_MAGIC)
+		kernmaxfilesize = (off_t)0x40000000 * sblock.fs_bsize - 1;
+	else
+		kernmaxfilesize = sblock.fs_maxfilesize;
+	if (filesize > kernmaxfilesize ||
+	    filesize > sblock.fs_maxfilesize ||
+	    (mode == IFDIR && filesize > MAXDIRSIZE)) {
+		if (debug)
+			printf("bad file size %ju:", (uintmax_t)filesize);
+		return (0);
+	}
+	return (1);
+}
+
 /*
  * Slow down IO so as to leave some disk bandwidth for other processes
  */
diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c
index 863bf34ff0fc..d328234220ad 100644
--- a/sbin/fsck_ffs/pass1.c
+++ b/sbin/fsck_ffs/pass1.c
@@ -256,7 +256,6 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuiltcg)
 {
 	struct inode ip;
 	union dinode *dp;
-	off_t kernmaxfilesize;
 	ufs2_daddr_t ndb;
 	mode_t mode;
 	intmax_t size, fixsize;
@@ -293,16 +292,7 @@ checkinode(ino_t inumber, struct inodesc *idesc, int rebuiltcg)
 		return (1);
 	}
 	lastino = inumber;
-	/* This should match the file size limit in ffs_mountfs(). */
-	if (sblock.fs_magic == FS_UFS1_MAGIC)
-		kernmaxfilesize = (off_t)0x40000000 * sblock.fs_bsize - 1;
-	else
-		kernmaxfilesize = sblock.fs_maxfilesize;
-	if (DIP(dp, di_size) > kernmaxfilesize ||
-	    DIP(dp, di_size) > sblock.fs_maxfilesize ||
-	    (mode == IFDIR && DIP(dp, di_size) > MAXDIRSIZE)) {
-		if (debug)
-			printf("bad size %ju:", (uintmax_t)DIP(dp, di_size));
+	if (chkfilesize(mode, DIP(dp, di_size)) == 0) {
 		pfatal("BAD FILE SIZE");
 		goto unknown;
 	}
diff --git a/sbin/fsck_ffs/suj.c b/sbin/fsck_ffs/suj.c
index 8bcee7c54c85..cbb6e597dfee 100644
--- a/sbin/fsck_ffs/suj.c
+++ b/sbin/fsck_ffs/suj.c
@@ -1963,6 +1963,9 @@ ino_build_trunc(struct jtrncrec *rec)
 		printf("ino_build_trunc: op %d ino %ju, size %jd\n",
 		    rec->jt_op, (uintmax_t)rec->jt_ino,
 		    (uintmax_t)rec->jt_size);
+	if (chkfilesize(IFREG, rec->jt_size) == 0)
+		err_suj("ino_build: truncation size too large %ju\n",
+		    (intmax_t)rec->jt_size);
 	sino = ino_lookup(rec->jt_ino, 1);
 	if (rec->jt_op == JOP_SYNC) {
 		sino->si_trunc = NULL;