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
This commit is contained in:
Kirk McKusick 2023-05-27 16:07:09 -07:00
parent c79a141695
commit 11ce203e05
4 changed files with 13 additions and 11 deletions

View file

@ -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;
}

View file

@ -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];

View file

@ -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)

View file

@ -371,7 +371,7 @@ pass2check(struct inodesc *idesc)
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 @@ pass2check(struct inodesc *idesc)
}
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) ||