git: 11ce203e0535 - main - Fix a bug in fsck_ffs(8) triggered by corrupted filesystems.

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Sat, 27 May 2023 23:08:13 UTC
The branch main has been updated by mckusick:

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

commit 11ce203e0535c1c8f520c9bda81ab9326cf5db80
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2023-05-27 23:07:09 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2023-05-27 23:07:09 +0000

    Fix a bug in fsck_ffs(8) triggered by corrupted filesystems.
    
    The last valid inode in the filesystem is maxino - 1, not maxino.
    Thus validity checks should ino < maxino, not ino <= maxino.
    
    Reported-by:  Robert Morris
    PR:           271312
    MFC-after:    1 week
    Sponsored-by: The FreeBSD Foundation
---
 sbin/fsck_ffs/dir.c    |  4 ++--
 sbin/fsck_ffs/fsutil.c |  2 +-
 sbin/fsck_ffs/inode.c  | 14 ++++++++------
 sbin/fsck_ffs/pass2.c  |  4 ++--
 4 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c
index e5f0e1e7e7f4..7ea471bcb30a 100644
--- a/sbin/fsck_ffs/dir.c
+++ b/sbin/fsck_ffs/dir.c
@@ -126,7 +126,7 @@ check_dirdepth(struct inoinfo *inp)
 	if (inp->i_depth == 0 && updateasked == 0) {
 		updateasked = 1;
 		if (preen) {
-			pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH");
+			pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH\n");
 			dirdepthupdate = 1;
 		} else {
 			/*
@@ -437,7 +437,7 @@ fileerror(ino_t cwd, ino_t ino, const char *errmesg)
 	char pathbuf[MAXPATHLEN + 1];
 
 	pwarn("%s ", errmesg);
-	if (ino < UFS_ROOTINO || ino > maxino) {
+	if (ino < UFS_ROOTINO || ino >= maxino) {
 		pfatal("out-of-range inode number %ju", (uintmax_t)ino);
 		return;
 	}
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
index 5e60aecf1826..7602203e6e90 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -170,7 +170,7 @@ inoinfo(ino_t inum)
 	struct inostatlist *ilp;
 	int iloff;
 
-	if (inum > maxino)
+	if (inum >= maxino)
 		errx(EEXIT, "inoinfo: inumber %ju out of range",
 		    (uintmax_t)inum);
 	ilp = &inostathead[inum / sblock.fs_ipg];
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index 00a60157138c..7dca95129ed1 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -433,8 +433,9 @@ void
 ginode(ino_t inumber, struct inode *ip)
 {
 	ufs2_daddr_t iblk;
+	struct ufs2_dinode *dp;
 
-	if (inumber < UFS_ROOTINO || inumber > maxino)
+	if (inumber < UFS_ROOTINO || inumber >= maxino)
 		errx(EEXIT, "bad inode number %ju to ginode",
 		    (uintmax_t)inumber);
 	ip->i_number = inumber;
@@ -473,14 +474,15 @@ ginode(ino_t inumber, struct inode *ip)
 	}
 	ip->i_dp = (union dinode *)
 	    &ip->i_bp->b_un.b_dinode2[inumber - ip->i_bp->b_index];
-	if (ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)ip->i_dp)) {
+	dp = (struct ufs2_dinode *)ip->i_dp;
+	/* Do not check hash of inodes being created */
+	if (dp->di_mode != 0 && ffs_verify_dinode_ckhash(&sblock, dp)) {
 		pwarn("INODE CHECK-HASH FAILED");
 		prtinode(ip);
 		if (preen || reply("FIX") != 0) {
 			if (preen)
 				printf(" (FIXED)\n");
-			ffs_update_dinode_ckhash(&sblock,
-			    (struct ufs2_dinode *)ip->i_dp);
+			ffs_update_dinode_ckhash(&sblock, dp);
 			inodirty(ip);
 		}
 	}
@@ -1292,7 +1294,7 @@ findino(struct inodesc *idesc)
 	if (dirp->d_ino == 0)
 		return (KEEPON);
 	if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
-	    dirp->d_ino >= UFS_ROOTINO && dirp->d_ino <= maxino) {
+	    dirp->d_ino >= UFS_ROOTINO && dirp->d_ino < maxino) {
 		idesc->id_parent = dirp->d_ino;
 		return (STOP|FOUND);
 	}
@@ -1322,7 +1324,7 @@ prtinode(struct inode *ip)
 
 	dp = ip->i_dp;
 	printf(" I=%lu ", (u_long)ip->i_number);
-	if (ip->i_number < UFS_ROOTINO || ip->i_number > maxino)
+	if (ip->i_number < UFS_ROOTINO || ip->i_number >= maxino)
 		return;
 	printf(" OWNER=");
 	if ((pw = getpwuid((int)DIP(dp, di_uid))) != NULL)
diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c
index 8200209cc03e..48bf27a8b142 100644
--- a/sbin/fsck_ffs/pass2.c
+++ b/sbin/fsck_ffs/pass2.c
@@ -371,7 +371,7 @@ chk1:
 		dirp->d_reclen = proto.d_reclen;
 	}
 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
-		if (dirp->d_ino > maxino) {
+		if (dirp->d_ino >= maxino) {
 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'");
 			/*
 			 * If we know parent set it now, otherwise let it
@@ -471,7 +471,7 @@ chk2:
 	}
 	idesc->id_entryno++;
 	n = 0;
-	if (dirp->d_ino > maxino) {
+	if (dirp->d_ino >= maxino) {
 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
 		n = reply("REMOVE");
 	} else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||