The branch main has been updated by mckusick:

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

commit 101a9ac07128a17d8797cc3e93978d2cfa457e99
Author:     Kirk McKusick <[email protected]>
AuthorDate: 2023-05-28 00:09:02 +0000
Commit:     Kirk McKusick <[email protected]>
CommitDate: 2023-05-28 00:12:30 +0000

    Fix a bug in fsck_ffs(8) triggered by corrupted filesystems.
    
    Check for valid file size before processing journal entries for it.
    Done by extracting the file size check from pass1.c into chkfilesize()
    then using it in the journal code in suj.c
    
    Reported-by:  Robert Morris
    PR:           271378
    MFC-after:    1 week
    Sponsored-by: The FreeBSD Foundation
---
 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 ad82c5f80da1..3b80169c1e3c 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -470,6 +470,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 7602203e6e90..5edc258d54bf 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -1207,6 +1207,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 8fed3d7723d6..d51e0ff4d83b 100644
--- a/sbin/fsck_ffs/suj.c
+++ b/sbin/fsck_ffs/suj.c
@@ -1965,6 +1965,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;

Reply via email to