svn commit: r350460 - in stable/12: sbin/fsck_ffs sbin/mount sys/sys sys/ufs/ffs sys/ufs/ufs

Kirk McKusick mckusick at FreeBSD.org
Wed Jul 31 00:16:16 UTC 2019


Author: mckusick
Date: Wed Jul 31 00:16:12 2019
New Revision: 350460
URL: https://svnweb.freebsd.org/changeset/base/350460

Log:
  MFC of 349589, 350070, 350071, 350096, and 350187
  
  Make filesystem-full messages limited per filesystem rather than systemwide
  Add "untrusted" option to mount command
  FS-14-UFS-3: when untrusted, valididate block pointers
  In fsck_ffs, treat any inode with bad content as unknown

Modified:
  stable/12/sbin/fsck_ffs/pass1.c
  stable/12/sbin/mount/mntopts.h
  stable/12/sbin/mount/mount.8
  stable/12/sbin/mount/mount.c
  stable/12/sys/sys/mount.h
  stable/12/sys/ufs/ffs/ffs_alloc.c
  stable/12/sys/ufs/ffs/ffs_balloc.c
  stable/12/sys/ufs/ffs/ffs_extern.h
  stable/12/sys/ufs/ffs/ffs_softdep.c
  stable/12/sys/ufs/ffs/ffs_subr.c
  stable/12/sys/ufs/ffs/ffs_vfsops.c
  stable/12/sys/ufs/ufs/ufs_bmap.c
  stable/12/sys/ufs/ufs/ufsmount.h
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sbin/fsck_ffs/pass1.c
==============================================================================
--- stable/12/sbin/fsck_ffs/pass1.c	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sbin/fsck_ffs/pass1.c	Wed Jul 31 00:16:12 2019	(r350460)
@@ -251,7 +251,7 @@ checkinode(ino_t inumber, struct inodesc *idesc, int r
 	int j, ret, offset;
 
 	if ((dp = getnextinode(inumber, rebuildcg)) == NULL)
-		return (0);
+		goto unknown;
 	mode = DIP(dp, di_mode) & IFMT;
 	if (mode == 0) {
 		if ((sblock.fs_magic == FS_UFS1_MAGIC &&

Modified: stable/12/sbin/mount/mntopts.h
==============================================================================
--- stable/12/sbin/mount/mntopts.h	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sbin/mount/mntopts.h	Wed Jul 31 00:16:12 2019	(r350460)
@@ -58,6 +58,7 @@ struct mntopt {
 #define MOPT_ACLS		{ "acls",	0, MNT_ACLS, 0 }
 #define MOPT_NFS4ACLS		{ "nfsv4acls",	0, MNT_NFS4ACLS, 0 }
 #define MOPT_AUTOMOUNTED	{ "automounted",0, MNT_AUTOMOUNTED, 0 }
+#define MOPT_UNTRUSTED		{ "untrusted",  0, MNT_UNTRUSTED, 0 }
 
 /* Control flags. */
 #define MOPT_FORCE		{ "force",	0, MNT_FORCE, 0 }
@@ -93,7 +94,8 @@ struct mntopt {
 	MOPT_MULTILABEL,						\
 	MOPT_ACLS,							\
 	MOPT_NFS4ACLS,							\
-	MOPT_AUTOMOUNTED
+	MOPT_AUTOMOUNTED,						\
+	MOPT_UNTRUSTED
 
 void getmntopts(const char *, const struct mntopt *, int *, int *);
 void rmslashes(char *, char *);

Modified: stable/12/sbin/mount/mount.8
==============================================================================
--- stable/12/sbin/mount/mount.8	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sbin/mount/mount.8	Wed Jul 31 00:16:12 2019	(r350460)
@@ -355,6 +355,12 @@ Lookups will be done in the mounted file system first.
 If those operations fail due to a non-existent file the underlying
 directory is then accessed.
 All creates are done in the mounted file system.
+.It Cm untrusted
+The file system is untrusted and the kernel should use more
+extensive checks on the file-system's metadata before using it.
+This option is intended to be used when mounting file systems
+from untrusted media such as USB memory sticks or other
+externally-provided media.
 .El
 .Pp
 Any additional options specific to a file system type that is not

Modified: stable/12/sbin/mount/mount.c
==============================================================================
--- stable/12/sbin/mount/mount.c	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sbin/mount/mount.c	Wed Jul 31 00:16:12 2019	(r350460)
@@ -118,6 +118,7 @@ static struct opt {
 	{ MNT_GJOURNAL,		"gjournal" },
 	{ MNT_AUTOMOUNTED,	"automounted" },
 	{ MNT_VERIFIED,		"verified" },
+	{ MNT_UNTRUSTED,	"untrusted" },
 	{ 0, NULL }
 };
 
@@ -972,6 +973,7 @@ flags2opts(int flags)
 	if (flags & MNT_MULTILABEL)	res = catopt(res, "multilabel");
 	if (flags & MNT_ACLS)		res = catopt(res, "acls");
 	if (flags & MNT_NFS4ACLS)	res = catopt(res, "nfsv4acls");
+	if (flags & MNT_UNTRUSTED)	res = catopt(res, "untrusted");
 
 	return (res);
 }

Modified: stable/12/sys/sys/mount.h
==============================================================================
--- stable/12/sys/sys/mount.h	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/sys/mount.h	Wed Jul 31 00:16:12 2019	(r350460)
@@ -296,6 +296,7 @@ void          __mnt_vnode_markerfree_active(struct vno
 #define	MNT_NOCLUSTERW	0x0000000080000000ULL /* disable cluster write */
 #define	MNT_SUJ		0x0000000100000000ULL /* using journaled soft updates */
 #define	MNT_AUTOMOUNTED	0x0000000200000000ULL /* mounted by automountd(8) */
+#define	MNT_UNTRUSTED	0x0000000800000000ULL /* filesys metadata untrusted */
 
 /*
  * NFS export related mount flags.
@@ -333,7 +334,8 @@ void          __mnt_vnode_markerfree_active(struct vno
 			MNT_NOCLUSTERW	| MNT_SUIDDIR	| MNT_SOFTDEP	| \
 			MNT_IGNORE	| MNT_EXPUBLIC	| MNT_NOSYMFOLLOW | \
 			MNT_GJOURNAL	| MNT_MULTILABEL | MNT_ACLS	| \
-			MNT_NFS4ACLS	| MNT_AUTOMOUNTED | MNT_VERIFIED)
+			MNT_NFS4ACLS	| MNT_AUTOMOUNTED | MNT_VERIFIED | \
+			MNT_UNTRUSTED)
 
 /* Mask of flags that can be updated. */
 #define	MNT_UPDATEMASK (MNT_NOSUID	| MNT_NOEXEC	| \
@@ -342,7 +344,7 @@ void          __mnt_vnode_markerfree_active(struct vno
 			MNT_NOSYMFOLLOW	| MNT_IGNORE	| \
 			MNT_NOCLUSTERR	| MNT_NOCLUSTERW | MNT_SUIDDIR	| \
 			MNT_ACLS	| MNT_USER	| MNT_NFS4ACLS	| \
-			MNT_AUTOMOUNTED)
+			MNT_AUTOMOUNTED | MNT_UNTRUSTED)
 
 /*
  * External filesystem command modifier flags.

Modified: stable/12/sys/ufs/ffs/ffs_alloc.c
==============================================================================
--- stable/12/sys/ufs/ffs/ffs_alloc.c	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/ufs/ffs/ffs_alloc.c	Wed Jul 31 00:16:12 2019	(r350460)
@@ -157,8 +157,6 @@ ffs_alloc(ip, lbn, bpref, size, flags, cred, bnp)
 	struct ufsmount *ump;
 	ufs2_daddr_t bno;
 	u_int cg, reclaimed;
-	static struct timeval lastfail;
-	static int curfail;
 	int64_t delta;
 #ifdef QUOTA
 	int error;
@@ -223,11 +221,14 @@ nospace:
 		softdep_request_cleanup(fs, ITOV(ip), cred, FLUSH_BLOCKS_WAIT);
 		goto retry;
 	}
-	UFS_UNLOCK(ump);
-	if (reclaimed > 0 && ppsratecheck(&lastfail, &curfail, 1)) {
+	if (reclaimed > 0 &&
+	    ppsratecheck(&ump->um_last_fullmsg, &ump->um_secs_fullmsg, 1)) {
+		UFS_UNLOCK(ump);
 		ffs_fserr(fs, ip->i_number, "filesystem full");
 		uprintf("\n%s: write failed, filesystem is full\n",
 		    fs->fs_fsmnt);
+	} else {
+		UFS_UNLOCK(ump);
 	}
 	return (ENOSPC);
 }
@@ -257,8 +258,6 @@ ffs_realloccg(ip, lbprev, bprev, bpref, osize, nsize, 
 	u_int cg, request, reclaimed;
 	int error, gbflags;
 	ufs2_daddr_t bno;
-	static struct timeval lastfail;
-	static int curfail;
 	int64_t delta;
 
 	vp = ITOV(ip);
@@ -448,14 +447,17 @@ nospace:
 		softdep_request_cleanup(fs, vp, cred, FLUSH_BLOCKS_WAIT);
 		goto retry;
 	}
-	UFS_UNLOCK(ump);
-	if (bp)
-		brelse(bp);
-	if (reclaimed > 0 && ppsratecheck(&lastfail, &curfail, 1)) {
+	if (reclaimed > 0 &&
+	    ppsratecheck(&ump->um_last_fullmsg, &ump->um_secs_fullmsg, 1)) {
+		UFS_UNLOCK(ump);
 		ffs_fserr(fs, ip->i_number, "filesystem full");
 		uprintf("\n%s: write failed, filesystem is full\n",
 		    fs->fs_fsmnt);
+	} else {
+		UFS_UNLOCK(ump);
 	}
+	if (bp)
+		brelse(bp);
 	return (ENOSPC);
 }
 
@@ -1098,8 +1100,6 @@ ffs_valloc(pvp, mode, cred, vpp)
 	ino_t ino, ipref;
 	u_int cg;
 	int error, error1, reclaimed;
-	static struct timeval lastfail;
-	static int curfail;
 
 	*vpp = NULL;
 	pip = VTOI(pvp);
@@ -1190,11 +1190,13 @@ noinodes:
 		softdep_request_cleanup(fs, pvp, cred, FLUSH_INODES_WAIT);
 		goto retry;
 	}
-	UFS_UNLOCK(ump);
-	if (ppsratecheck(&lastfail, &curfail, 1)) {
+	if (ppsratecheck(&ump->um_last_fullmsg, &ump->um_secs_fullmsg, 1)) {
+		UFS_UNLOCK(ump);
 		ffs_fserr(fs, pip->i_number, "out of inodes");
 		uprintf("\n%s: create/symlink failed, no inodes free\n",
 		    fs->fs_fsmnt);
+	} else {
+		UFS_UNLOCK(ump);
 	}
 	return (ENOSPC);
 }
@@ -1369,7 +1371,7 @@ ffs_blkpref_ufs1(ip, lbn, indx, bap)
 	struct fs *fs;
 	u_int cg, inocg;
 	u_int avgbfree, startcg;
-	ufs2_daddr_t pref;
+	ufs2_daddr_t pref, prevbn;
 
 	KASSERT(indx <= 0 || bap != NULL, ("need non-NULL bap"));
 	mtx_assert(UFS_MTX(ITOUMP(ip)), MA_OWNED);
@@ -1419,7 +1421,15 @@ ffs_blkpref_ufs1(ip, lbn, indx, bap)
 	 * have a block allocated immediately preceding us, then we need
 	 * to decide where to start allocating new blocks.
 	 */
-	if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+	if (indx ==  0) {
+		prevbn = 0;
+	} else {
+		prevbn = bap[indx - 1];
+		if (UFS_CHECK_BLKNO(ITOVFS(ip), ip->i_number, prevbn,
+		    fs->fs_bsize) != 0)
+			prevbn = 0;
+	}
+	if (indx % fs->fs_maxbpg == 0 || prevbn == 0) {
 		/*
 		 * If we are allocating a directory data block, we want
 		 * to place it in the metadata area.
@@ -1437,10 +1447,10 @@ ffs_blkpref_ufs1(ip, lbn, indx, bap)
 		 * Find a cylinder with greater than average number of
 		 * unused data blocks.
 		 */
-		if (indx == 0 || bap[indx - 1] == 0)
+		if (indx == 0 || prevbn == 0)
 			startcg = inocg + lbn / fs->fs_maxbpg;
 		else
-			startcg = dtog(fs, bap[indx - 1]) + 1;
+			startcg = dtog(fs, prevbn) + 1;
 		startcg %= fs->fs_ncg;
 		avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
 		for (cg = startcg; cg < fs->fs_ncg; cg++)
@@ -1458,7 +1468,7 @@ ffs_blkpref_ufs1(ip, lbn, indx, bap)
 	/*
 	 * Otherwise, we just always try to lay things out contiguously.
 	 */
-	return (bap[indx - 1] + fs->fs_frag);
+	return (prevbn + fs->fs_frag);
 }
 
 /*
@@ -1474,7 +1484,7 @@ ffs_blkpref_ufs2(ip, lbn, indx, bap)
 	struct fs *fs;
 	u_int cg, inocg;
 	u_int avgbfree, startcg;
-	ufs2_daddr_t pref;
+	ufs2_daddr_t pref, prevbn;
 
 	KASSERT(indx <= 0 || bap != NULL, ("need non-NULL bap"));
 	mtx_assert(UFS_MTX(ITOUMP(ip)), MA_OWNED);
@@ -1524,7 +1534,15 @@ ffs_blkpref_ufs2(ip, lbn, indx, bap)
 	 * have a block allocated immediately preceding us, then we need
 	 * to decide where to start allocating new blocks.
 	 */
-	if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+	if (indx ==  0) {
+		prevbn = 0;
+	} else {
+		prevbn = bap[indx - 1];
+		if (UFS_CHECK_BLKNO(ITOVFS(ip), ip->i_number, prevbn,
+		    fs->fs_bsize) != 0)
+			prevbn = 0;
+	}
+	if (indx % fs->fs_maxbpg == 0 || prevbn == 0) {
 		/*
 		 * If we are allocating a directory data block, we want
 		 * to place it in the metadata area.
@@ -1542,10 +1560,10 @@ ffs_blkpref_ufs2(ip, lbn, indx, bap)
 		 * Find a cylinder with greater than average number of
 		 * unused data blocks.
 		 */
-		if (indx == 0 || bap[indx - 1] == 0)
+		if (indx == 0 || prevbn == 0)
 			startcg = inocg + lbn / fs->fs_maxbpg;
 		else
-			startcg = dtog(fs, bap[indx - 1]) + 1;
+			startcg = dtog(fs, prevbn) + 1;
 		startcg %= fs->fs_ncg;
 		avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
 		for (cg = startcg; cg < fs->fs_ncg; cg++)
@@ -1563,7 +1581,7 @@ ffs_blkpref_ufs2(ip, lbn, indx, bap)
 	/*
 	 * Otherwise, we just always try to lay things out contiguously.
 	 */
-	return (bap[indx - 1] + fs->fs_frag);
+	return (prevbn + fs->fs_frag);
 }
 
 /*

Modified: stable/12/sys/ufs/ffs/ffs_balloc.c
==============================================================================
--- stable/12/sys/ufs/ffs/ffs_balloc.c	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/ufs/ffs/ffs_balloc.c	Wed Jul 31 00:16:12 2019	(r350460)
@@ -99,6 +99,7 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, i
 	struct fs *fs;
 	ufs1_daddr_t nb;
 	struct buf *bp, *nbp;
+	struct mount *mp;
 	struct ufsmount *ump;
 	struct indir indirs[UFS_NIADDR + 2];
 	int deallocated, osize, nsize, num, i, error;
@@ -108,13 +109,12 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, i
 	ufs2_daddr_t *lbns_remfree, lbns[UFS_NIADDR + 1];
 	int unwindidx = -1;
 	int saved_inbdflush;
-	static struct timeval lastfail;
-	static int curfail;
 	int gbflags, reclaimed;
 
 	ip = VTOI(vp);
 	dp = ip->i_din1;
 	fs = ITOFS(ip);
+	mp = ITOVFS(ip);
 	ump = ITOUMP(ip);
 	lbn = lblkno(fs, startoffset);
 	size = blkoff(fs, startoffset) + size;
@@ -297,6 +297,11 @@ retry:
 		}
 		bap = (ufs1_daddr_t *)bp->b_data;
 		nb = bap[indirs[i].in_off];
+		if ((error = UFS_CHECK_BLKNO(mp, ip->i_number, nb,
+		    fs->fs_bsize)) != 0) {
+			brelse(bp);
+			goto fail;
+		}
 		if (i == num)
 			break;
 		i += 1;
@@ -315,17 +320,21 @@ retry:
 		if ((error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
 		    flags | IO_BUFLOCKED, cred, &newb)) != 0) {
 			brelse(bp);
+			UFS_LOCK(ump);
 			if (DOINGSOFTDEP(vp) && ++reclaimed == 1) {
-				UFS_LOCK(ump);
 				softdep_request_cleanup(fs, vp, cred,
 				    FLUSH_BLOCKS_WAIT);
 				UFS_UNLOCK(ump);
 				goto retry;
 			}
-			if (ppsratecheck(&lastfail, &curfail, 1)) {
+			if (ppsratecheck(&ump->um_last_fullmsg,
+			    &ump->um_secs_fullmsg, 1)) {
+				UFS_UNLOCK(ump);
 				ffs_fserr(fs, ip->i_number, "filesystem full");
 				uprintf("\n%s: write failed, filesystem "
 				    "is full\n", fs->fs_fsmnt);
+			} else {
+				UFS_UNLOCK(ump);
 			}
 			goto fail;
 		}
@@ -394,17 +403,21 @@ retry:
 		    flags | IO_BUFLOCKED, cred, &newb);
 		if (error) {
 			brelse(bp);
+			UFS_LOCK(ump);
 			if (DOINGSOFTDEP(vp) && ++reclaimed == 1) {
-				UFS_LOCK(ump);
 				softdep_request_cleanup(fs, vp, cred,
 				    FLUSH_BLOCKS_WAIT);
 				UFS_UNLOCK(ump);
 				goto retry;
 			}
-			if (ppsratecheck(&lastfail, &curfail, 1)) {
+			if (ppsratecheck(&ump->um_last_fullmsg,
+			    &ump->um_secs_fullmsg, 1)) {
+				UFS_UNLOCK(ump);
 				ffs_fserr(fs, ip->i_number, "filesystem full");
 				uprintf("\n%s: write failed, filesystem "
 				    "is full\n", fs->fs_fsmnt);
+			} else {
+				UFS_UNLOCK(ump);
 			}
 			goto fail;
 		}
@@ -574,6 +587,7 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, i
 	ufs_lbn_t lbn, lastlbn;
 	struct fs *fs;
 	struct buf *bp, *nbp;
+	struct mount *mp;
 	struct ufsmount *ump;
 	struct indir indirs[UFS_NIADDR + 2];
 	ufs2_daddr_t nb, newb, *bap, pref;
@@ -582,13 +596,12 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, i
 	int deallocated, osize, nsize, num, i, error;
 	int unwindidx = -1;
 	int saved_inbdflush;
-	static struct timeval lastfail;
-	static int curfail;
 	int gbflags, reclaimed;
 
 	ip = VTOI(vp);
 	dp = ip->i_din2;
 	fs = ITOFS(ip);
+	mp = ITOVFS(ip);
 	ump = ITOUMP(ip);
 	lbn = lblkno(fs, startoffset);
 	size = blkoff(fs, startoffset) + size;
@@ -884,6 +897,11 @@ retry:
 		}
 		bap = (ufs2_daddr_t *)bp->b_data;
 		nb = bap[indirs[i].in_off];
+		if ((error = UFS_CHECK_BLKNO(mp, ip->i_number, nb,
+		    fs->fs_bsize)) != 0) {
+			brelse(bp);
+			goto fail;
+		}
 		if (i == num)
 			break;
 		i += 1;
@@ -902,17 +920,21 @@ retry:
 		if ((error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
 		    flags | IO_BUFLOCKED, cred, &newb)) != 0) {
 			brelse(bp);
+			UFS_LOCK(ump);
 			if (DOINGSOFTDEP(vp) && ++reclaimed == 1) {
-				UFS_LOCK(ump);
 				softdep_request_cleanup(fs, vp, cred,
 				    FLUSH_BLOCKS_WAIT);
 				UFS_UNLOCK(ump);
 				goto retry;
 			}
-			if (ppsratecheck(&lastfail, &curfail, 1)) {
+			if (ppsratecheck(&ump->um_last_fullmsg,
+			    &ump->um_secs_fullmsg, 1)) {
+				UFS_UNLOCK(ump);
 				ffs_fserr(fs, ip->i_number, "filesystem full");
 				uprintf("\n%s: write failed, filesystem "
 				    "is full\n", fs->fs_fsmnt);
+			} else {
+				UFS_UNLOCK(ump);
 			}
 			goto fail;
 		}
@@ -982,17 +1004,21 @@ retry:
 		    flags | IO_BUFLOCKED, cred, &newb);
 		if (error) {
 			brelse(bp);
+			UFS_LOCK(ump);
 			if (DOINGSOFTDEP(vp) && ++reclaimed == 1) {
-				UFS_LOCK(ump);
 				softdep_request_cleanup(fs, vp, cred,
 				    FLUSH_BLOCKS_WAIT);
 				UFS_UNLOCK(ump);
 				goto retry;
 			}
-			if (ppsratecheck(&lastfail, &curfail, 1)) {
+			if (ppsratecheck(&ump->um_last_fullmsg,
+			    &ump->um_secs_fullmsg, 1)) {
+				UFS_UNLOCK(ump);
 				ffs_fserr(fs, ip->i_number, "filesystem full");
 				uprintf("\n%s: write failed, filesystem "
 				    "is full\n", fs->fs_fsmnt);
+			} else {
+				UFS_UNLOCK(ump);
 			}
 			goto fail;
 		}

Modified: stable/12/sys/ufs/ffs/ffs_extern.h
==============================================================================
--- stable/12/sys/ufs/ffs/ffs_extern.h	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/ufs/ffs/ffs_extern.h	Wed Jul 31 00:16:12 2019	(r350460)
@@ -68,6 +68,7 @@ ufs2_daddr_t ffs_blkpref_ufs1(struct inode *, ufs_lbn_
 ufs2_daddr_t ffs_blkpref_ufs2(struct inode *, ufs_lbn_t, int, ufs2_daddr_t *);
 void	ffs_blkrelease_finish(struct ufsmount *, u_long);
 u_long	ffs_blkrelease_start(struct ufsmount *, struct vnode *, ino_t);
+int	ffs_check_blkno(struct mount *, ino_t, ufs2_daddr_t, int);
 int	ffs_checkfreefile(struct fs *, struct vnode *, ino_t);
 void	ffs_clrblock(struct fs *, u_char *, ufs1_daddr_t);
 void	ffs_clusteracct(struct fs *, struct cg *, ufs1_daddr_t, int);

Modified: stable/12/sys/ufs/ffs/ffs_softdep.c
==============================================================================
--- stable/12/sys/ufs/ffs/ffs_softdep.c	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/ufs/ffs/ffs_softdep.c	Wed Jul 31 00:16:12 2019	(r350460)
@@ -8109,6 +8109,7 @@ indir_trunc(freework, dbn, lbn)
 	struct buf *bp;
 	struct fs *fs;
 	struct indirdep *indirdep;
+	struct mount *mp;
 	struct ufsmount *ump;
 	ufs1_daddr_t *bap1;
 	ufs2_daddr_t nb, nnb, *bap2;
@@ -8118,7 +8119,8 @@ indir_trunc(freework, dbn, lbn)
 	int goingaway, freedeps, needj, level, cnt, i;
 
 	freeblks = freework->fw_freeblks;
-	ump = VFSTOUFS(freeblks->fb_list.wk_mp);
+	mp = freeblks->fb_list.wk_mp;
+	ump = VFSTOUFS(mp);
 	fs = ump->um_fs;
 	/*
 	 * Get buffer of block pointers to be freed.  There are three cases:
@@ -8211,6 +8213,9 @@ indir_trunc(freework, dbn, lbn)
 	 */
 	key = ffs_blkrelease_start(ump, freeblks->fb_devvp, freeblks->fb_inum);
 	for (i = freework->fw_off; i < NINDIR(fs); i++, nb = nnb) {
+		if (UFS_CHECK_BLKNO(mp, freeblks->fb_inum, nb,
+		    fs->fs_bsize) != 0)
+			nb = 0;
 		if (i != NINDIR(fs) - 1) {
 			if (ufs1fmt)
 				nnb = bap1[i+1];

Modified: stable/12/sys/ufs/ffs/ffs_subr.c
==============================================================================
--- stable/12/sys/ufs/ffs/ffs_subr.c	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/ufs/ffs/ffs_subr.c	Wed Jul 31 00:16:12 2019	(r350460)
@@ -133,7 +133,56 @@ ffs_load_inode(struct buf *bp, struct inode *ip, struc
 		ip->i_gid = ip->i_din2->di_gid;
 	}
 }
-#endif /* KERNEL */
+
+/*
+ * Verify that a filesystem block number is a valid data block.
+ * This routine is only called on untrusted filesystems.
+ */
+int
+ffs_check_blkno(struct mount *mp, ino_t inum, ufs2_daddr_t daddr, int blksize)
+{
+	struct fs *fs;
+	struct ufsmount *ump;
+	ufs2_daddr_t end_daddr;
+	int cg, havemtx;
+
+	KASSERT((mp->mnt_flag & MNT_UNTRUSTED) != 0,
+	    ("ffs_check_blkno called on a trusted file system"));
+	ump = VFSTOUFS(mp);
+	fs = ump->um_fs;
+	cg = dtog(fs, daddr);
+	end_daddr = daddr + numfrags(fs, blksize);
+	/*
+	 * Verify that the block number is a valid data block. Also check
+	 * that it does not point to an inode block or a superblock. Accept
+	 * blocks that are unalloacted (0) or part of snapshot metadata
+	 * (BLK_NOCOPY or BLK_SNAP).
+	 *
+	 * Thus, the block must be in a valid range for the filesystem and
+	 * either in the space before a backup superblock (except the first
+	 * cylinder group where that space is used by the bootstrap code) or
+	 * after the inode blocks and before the end of the cylinder group.
+	 */
+	if ((uint64_t)daddr <= BLK_SNAP ||
+	    ((uint64_t)end_daddr <= fs->fs_size &&
+	    ((cg > 0 && end_daddr <= cgsblock(fs, cg)) ||
+	    (daddr >= cgdmin(fs, cg) &&
+	    end_daddr <= cgbase(fs, cg) + fs->fs_fpg))))
+		return (0);
+	if ((havemtx = mtx_owned(UFS_MTX(ump))) == 0)
+		UFS_LOCK(ump);
+	if (ppsratecheck(&ump->um_last_integritymsg,
+	    &ump->um_secs_integritymsg, 1)) {
+		UFS_UNLOCK(ump);
+		uprintf("\n%s: inode %jd, out-of-range indirect block "
+		    "number %jd\n", mp->mnt_stat.f_mntonname, inum, daddr);
+		if (havemtx)
+			UFS_LOCK(ump);
+	} else if (!havemtx)
+		UFS_UNLOCK(ump);
+	return (EIO);
+}
+#endif /* _KERNEL */
 
 /*
  * These are the low-level functions that actually read and write

Modified: stable/12/sys/ufs/ffs/ffs_vfsops.c
==============================================================================
--- stable/12/sys/ufs/ffs/ffs_vfsops.c	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/ufs/ffs/ffs_vfsops.c	Wed Jul 31 00:16:12 2019	(r350460)
@@ -143,7 +143,7 @@ static struct buf_ops ffs_ops = {
 static const char *ffs_opts[] = { "acls", "async", "noatime", "noclusterr",
     "noclusterw", "noexec", "export", "force", "from", "groupquota",
     "multilabel", "nfsv4acls", "fsckpid", "snapshot", "nosuid", "suiddir",
-    "nosymfollow", "sync", "union", "userquota", NULL };
+    "nosymfollow", "sync", "union", "userquota", "untrusted", NULL };
 
 static int
 ffs_mount(struct mount *mp)
@@ -182,6 +182,9 @@ ffs_mount(struct mount *mp)
 		return (error);
 
 	mntorflags = 0;
+	if (vfs_getopt(mp->mnt_optnew, "untrusted", NULL, NULL) == 0)
+		mntorflags |= MNT_UNTRUSTED;
+
 	if (vfs_getopt(mp->mnt_optnew, "acls", NULL, NULL) == 0)
 		mntorflags |= MNT_ACLS;
 
@@ -911,6 +914,10 @@ ffs_mountfs(devvp, mp, td)
 	ump->um_ifree = ffs_ifree;
 	ump->um_rdonly = ffs_rdonly;
 	ump->um_snapgone = ffs_snapgone;
+	if ((mp->mnt_flag & MNT_UNTRUSTED) != 0)
+		ump->um_check_blkno = ffs_check_blkno;
+	else
+		ump->um_check_blkno = NULL;
 	mtx_init(UFS_MTX(ump), "FFS", "FFS Lock", MTX_DEF);
 	ffs_oldfscompat_read(fs, ump, fs->fs_sblockloc);
 	fs->fs_ronly = ronly;

Modified: stable/12/sys/ufs/ufs/ufs_bmap.c
==============================================================================
--- stable/12/sys/ufs/ufs/ufs_bmap.c	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/ufs/ufs/ufs_bmap.c	Wed Jul 31 00:16:12 2019	(r350460)
@@ -264,8 +264,16 @@ ufs_bmaparray(vp, bn, bnp, nbp, runp, runb)
 		if (error != 0)
 			return (error);
 
-		if (I_IS_UFS1(ip)) {
+		if (I_IS_UFS1(ip))
 			daddr = ((ufs1_daddr_t *)bp->b_data)[ap->in_off];
+		else
+			daddr = ((ufs2_daddr_t *)bp->b_data)[ap->in_off];
+		if ((error = UFS_CHECK_BLKNO(mp, ip->i_number, daddr,
+		     mp->mnt_stat.f_iosize)) != 0) {
+			bqrelse(bp);
+			return (error);
+		}
+		if (I_IS_UFS1(ip)) {
 			if (num == 1 && daddr && runp) {
 				for (bn = ap->in_off + 1;
 				    bn < MNINDIR(ump) && *runp < maxrun &&
@@ -284,7 +292,6 @@ ufs_bmaparray(vp, bn, bnp, nbp, runp, runb)
 			}
 			continue;
 		}
-		daddr = ((ufs2_daddr_t *)bp->b_data)[ap->in_off];
 		if (num == 1 && daddr && runp) {
 			for (bn = ap->in_off + 1;
 			    bn < MNINDIR(ump) && *runp < maxrun &&

Modified: stable/12/sys/ufs/ufs/ufsmount.h
==============================================================================
--- stable/12/sys/ufs/ufs/ufsmount.h	Tue Jul 30 23:50:49 2019	(r350459)
+++ stable/12/sys/ufs/ufs/ufsmount.h	Wed Jul 31 00:16:12 2019	(r350460)
@@ -100,6 +100,10 @@ struct ufsmount {
 	char	um_qflags[MAXQUOTAS];		/* (i) quota specific flags */
 	int64_t	um_savedmaxfilesize;		/* (c) track maxfilesize */
 	u_int	um_flags;			/* (i) filesystem flags */
+	struct	timeval um_last_fullmsg;	/* (i) last full msg time */
+	int	um_secs_fullmsg;		/* (i) seconds since full msg */
+	struct	timeval um_last_integritymsg;	/* (i) last integrity msg */
+	int	um_secs_integritymsg;		/* (i) secs since integ msg */
 	u_int	um_trim_inflight;		/* (i) outstanding trim count */
 	u_int	um_trim_inflight_blks;		/* (i) outstanding trim blks */
 	u_long	um_trim_total;			/* (i) total trim count */
@@ -119,6 +123,7 @@ struct ufsmount {
 	void	(*um_ifree)(struct ufsmount *, struct inode *);
 	int	(*um_rdonly)(struct inode *);
 	void	(*um_snapgone)(struct inode *);
+	int	(*um_check_blkno)(struct mount *, ino_t, daddr_t, int);
 };
 
 /*
@@ -130,15 +135,22 @@ struct ufsmount {
 /*
  * function prototypes
  */
-#define	UFS_BALLOC(aa, bb, cc, dd, ee, ff) VFSTOUFS((aa)->v_mount)->um_balloc(aa, bb, cc, dd, ee, ff)
-#define	UFS_BLKATOFF(aa, bb, cc, dd) VFSTOUFS((aa)->v_mount)->um_blkatoff(aa, bb, cc, dd)
-#define	UFS_TRUNCATE(aa, bb, cc, dd) VFSTOUFS((aa)->v_mount)->um_truncate(aa, bb, cc, dd)
+#define	UFS_BALLOC(aa, bb, cc, dd, ee, ff) \
+	VFSTOUFS((aa)->v_mount)->um_balloc(aa, bb, cc, dd, ee, ff)
+#define	UFS_BLKATOFF(aa, bb, cc, dd) \
+	VFSTOUFS((aa)->v_mount)->um_blkatoff(aa, bb, cc, dd)
+#define	UFS_TRUNCATE(aa, bb, cc, dd) \
+	VFSTOUFS((aa)->v_mount)->um_truncate(aa, bb, cc, dd)
 #define	UFS_UPDATE(aa, bb) VFSTOUFS((aa)->v_mount)->um_update(aa, bb)
-#define	UFS_VALLOC(aa, bb, cc, dd) VFSTOUFS((aa)->v_mount)->um_valloc(aa, bb, cc, dd)
+#define	UFS_VALLOC(aa, bb, cc, dd) \
+	VFSTOUFS((aa)->v_mount)->um_valloc(aa, bb, cc, dd)
 #define	UFS_VFREE(aa, bb, cc) VFSTOUFS((aa)->v_mount)->um_vfree(aa, bb, cc)
 #define	UFS_IFREE(aa, bb) ((aa)->um_ifree(aa, bb))
 #define	UFS_RDONLY(aa) (ITOUMP(aa)->um_rdonly(aa))
 #define	UFS_SNAPGONE(aa) (ITOUMP(aa)->um_snapgone(aa))
+#define	UFS_CHECK_BLKNO(aa, bb, cc, dd) 		\
+	(VFSTOUFS(aa)->um_check_blkno == NULL ? 0 :	\
+	 VFSTOUFS(aa)->um_check_blkno(aa, bb, cc, dd))
 
 #define	UFS_LOCK(aa)	mtx_lock(&(aa)->um_lock)
 #define	UFS_UNLOCK(aa)	mtx_unlock(&(aa)->um_lock)


More information about the svn-src-all mailing list