From owner-svn-src-all@freebsd.org Wed Jul 31 00:16:15 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id F09D8B0E68; Wed, 31 Jul 2019 00:16:15 +0000 (UTC) (envelope-from mckusick@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id DE1BF87121; Wed, 31 Jul 2019 00:16:15 +0000 (UTC) (envelope-from mckusick@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id AFA5C211B8; Wed, 31 Jul 2019 00:16:15 +0000 (UTC) (envelope-from mckusick@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x6V0GFqi077068; Wed, 31 Jul 2019 00:16:15 GMT (envelope-from mckusick@FreeBSD.org) Received: (from mckusick@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x6V0GD7T077056; Wed, 31 Jul 2019 00:16:13 GMT (envelope-from mckusick@FreeBSD.org) Message-Id: <201907310016.x6V0GD7T077056@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mckusick set sender to mckusick@FreeBSD.org using -f From: Kirk McKusick Date: Wed, 31 Jul 2019 00:16:13 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r350460 - in stable/12: sbin/fsck_ffs sbin/mount sys/sys sys/ufs/ffs sys/ufs/ufs X-SVN-Group: stable-12 X-SVN-Commit-Author: mckusick X-SVN-Commit-Paths: in stable/12: sbin/fsck_ffs sbin/mount sys/sys sys/ufs/ffs sys/ufs/ufs X-SVN-Commit-Revision: 350460 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: DE1BF87121 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.96 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.996,0]; NEURAL_HAM_SHORT(-0.96)[-0.963,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US]; NEURAL_HAM_LONG(-1.00)[-1.000,0] X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 31 Jul 2019 00:16:16 -0000 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)