git: bb9f1ba4b55c - main - Add more accurate directory entries check

From: Fedor Uporov <fsu_at_FreeBSD.org>
Date: Thu, 30 Dec 2021 06:15:18 UTC
The branch main has been updated by fsu:

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

commit bb9f1ba4b55c1f566d59cc7c7d1d28dd37715984
Author:     Fedor Uporov <fsu@FreeBSD.org>
AuthorDate: 2021-10-29 12:45:50 +0000
Commit:     Fedor Uporov <fsu@FreeBSD.org>
CommitDate: 2021-12-30 06:14:44 +0000

    Add more accurate directory entries check
    
    Rename ext2_dirbadentry() to ext2_check_direntry(). Add directory
    entry inode value check, and call ext2_check_direntry() in all cases.
    The dirchk sysctl is removed.
    
    PR:                     259024,259041
    Reported by:            Robert Morris
    Reviewed by:            pfg
    MFC after:              2 weeks
    Differential Revision:  https://reviews.freebsd.org/D33374
---
 sys/fs/ext2fs/ext2_lookup.c | 39 ++++++++++-----------------------------
 1 file changed, 10 insertions(+), 29 deletions(-)

diff --git a/sys/fs/ext2fs/ext2_lookup.c b/sys/fs/ext2fs/ext2_lookup.c
index 3294ad3401ff..a8e28e24f9e2 100644
--- a/sys/fs/ext2fs/ext2_lookup.c
+++ b/sys/fs/ext2fs/ext2_lookup.c
@@ -79,15 +79,8 @@ SDT_PROBE_DEFINE4(ext2fs, , trace, ext2_dirbad_error,
 SDT_PROBE_DEFINE5(ext2fs, , trace, ext2_dirbadentry_error,
     "char*", "int", "uint32_t", "uint16_t", "uint8_t");
 
-#ifdef INVARIANTS
-static int dirchk = 1;
-#else
-static int dirchk = 0;
-#endif
-
 static SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
     "EXT2FS filesystem");
-SYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
 
 /*
    DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
@@ -129,8 +122,8 @@ static u_char dt_to_ext2_ft[] = {
 #define	DTTOFT(dt) \
     ((dt) < nitems(dt_to_ext2_ft) ? dt_to_ext2_ft[(dt)] : EXT2_FT_UNKNOWN)
 
-static int	ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
-		    int entryoffsetinblock);
+static int	ext2_check_direntry(struct vnode *dp,
+		    struct ext2fs_direct_2 *de, int entryoffsetinblock);
 static int	ext2_is_dot_entry(struct componentname *cnp);
 static int	ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp,
 		    struct componentname *cnp, ino_t *dd_ino);
@@ -734,20 +727,14 @@ ext2_search_dirblock(struct inode *ip, void *data, int *foundp,
 	ep = (struct ext2fs_direct_2 *)((char *)data + offset);
 	top = (struct ext2fs_direct_2 *)((char *)data + bsize);
 	while (ep < top) {
-		/*
-		 * Full validation checks are slow, so we only check
-		 * enough to insure forward progress through the
-		 * directory. Complete checks can be run by setting
-		 * "vfs.e2fs.dirchk" to be true.
-		 */
-		if (le16toh(ep->e2d_reclen) == 0 ||
-		    (dirchk && ext2_dirbadentry(vdp, ep, offset))) {
+		if (ext2_check_direntry(vdp, ep, offset)) {
 			int i;
 
 			ext2_dirbad(ip, *offp, "mangled entry");
 			i = bsize - (offset & (bsize - 1));
 			*offp += i;
 			offset += i;
+			ep = (struct ext2fs_direct_2 *)((char *)data + offset);
 			continue;
 		}
 
@@ -836,15 +823,11 @@ ext2_dirbad(struct inode *ip, doff_t offset, char *how)
  *	name is not longer than MAXNAMLEN
  *	name must be as long as advertised, and null terminated
  */
-/*
- *	changed so that it confirms to ext2_check_dir_entry
- */
 static int
-ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
+ext2_check_direntry(struct vnode *dp, struct ext2fs_direct_2 *de,
     int entryoffsetinblock)
 {
-	int DIRBLKSIZ = VTOI(dp)->i_e2fs->e2fs_bsize;
-
+	struct m_ext2fs *fs = VTOI(dp)->i_e2fs;
 	char *error_msg = NULL;
 
 	if (le16toh(de->e2d_reclen) < EXT2_DIR_REC_LEN(1))
@@ -853,12 +836,10 @@ ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
 		error_msg = "rec_len % 4 != 0";
 	else if (le16toh(de->e2d_reclen) < EXT2_DIR_REC_LEN(de->e2d_namlen))
 		error_msg = "reclen is too small for name_len";
-	else if (entryoffsetinblock + le16toh(de->e2d_reclen)> DIRBLKSIZ)
+	else if (entryoffsetinblock + le16toh(de->e2d_reclen)> fs->e2fs_bsize)
 		error_msg = "directory entry across blocks";
-	/* else LATER
-	     if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
-		error_msg = "inode out of bounds";
-	*/
+	else if (le32toh(de->e2d_ino) > fs->e2fs->e2fs_icount)
+		error_msg = "directory entry inode out of bounds";
 
 	if (error_msg != NULL) {
 		SDT_PROBE5(ext2fs, , trace, ext2_dirbadentry_error,
@@ -866,7 +847,7 @@ ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
 		    le32toh(de->e2d_ino), le16toh(de->e2d_reclen),
 		    de->e2d_namlen);
 	}
-	return (error_msg == NULL ? 0 : 1);
+	return (error_msg == NULL ? 0 : EINVAL);
 }
 
 /*