From owner-svn-src-all@freebsd.org Wed Jul 17 22:07:45 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 9458BB697D; Wed, 17 Jul 2019 22:07:45 +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 760AE95C45; Wed, 17 Jul 2019 22:07:45 +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 4C70225097; Wed, 17 Jul 2019 22:07:45 +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 x6HM7jMn090407; Wed, 17 Jul 2019 22:07:45 GMT (envelope-from mckusick@FreeBSD.org) Received: (from mckusick@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x6HM7hbS090400; Wed, 17 Jul 2019 22:07:43 GMT (envelope-from mckusick@FreeBSD.org) Message-Id: <201907172207.x6HM7hbS090400@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mckusick set sender to mckusick@FreeBSD.org using -f From: Kirk McKusick Date: Wed, 17 Jul 2019 22:07:43 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r350096 - in head/sys/ufs: ffs ufs X-SVN-Group: head X-SVN-Commit-Author: mckusick X-SVN-Commit-Paths: in head/sys/ufs: ffs ufs X-SVN-Commit-Revision: 350096 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 760AE95C45 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.98 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_SHORT(-0.98)[-0.979,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] 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, 17 Jul 2019 22:07:45 -0000 Author: mckusick Date: Wed Jul 17 22:07:43 2019 New Revision: 350096 URL: https://svnweb.freebsd.org/changeset/base/350096 Log: The error reported in FS-14-UFS-3 can only happen on UFS/FFS filesystems that have block pointers that are out-of-range for their filesystem. These out-of-range block pointers are corrected by fsck(8) so are only encountered when an unchecked filesystem is mounted. A new "untrusted" flag has been added to the generic mount interface that can be set when mounting media of unknown provenance or integrity. For example, a daemon that automounts a filesystem on a flash drive when it is plugged into a system. This commit adds a test to UFS/FFS that validates all block numbers before using them. Because checking for out-of-range blocks adds unnecessary overhead to normal operation, the tests are only done when the filesystem is mounted as an "untrusted" filesystem. Reported by: Christopher Krah, Thomas Barabosch, and Jan-Niclas Hilgert of Fraunhofer FKIE Reported as: FS-14-UFS-3: Out of bounds read in write-2 (ffs_alloccg) Reviewed by: kib Sponsored by: Netflix Modified: head/sys/ufs/ffs/ffs_alloc.c head/sys/ufs/ffs/ffs_balloc.c head/sys/ufs/ffs/ffs_extern.h head/sys/ufs/ffs/ffs_softdep.c head/sys/ufs/ffs/ffs_subr.c head/sys/ufs/ffs/ffs_vfsops.c head/sys/ufs/ufs/ufs_bmap.c head/sys/ufs/ufs/ufsmount.h Modified: head/sys/ufs/ffs/ffs_alloc.c ============================================================================== --- head/sys/ufs/ffs/ffs_alloc.c Wed Jul 17 21:25:26 2019 (r350095) +++ head/sys/ufs/ffs/ffs_alloc.c Wed Jul 17 22:07:43 2019 (r350096) @@ -1374,7 +1374,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); @@ -1424,7 +1424,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. @@ -1442,10 +1450,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++) @@ -1463,7 +1471,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); } /* @@ -1479,7 +1487,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); @@ -1529,7 +1537,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. @@ -1547,10 +1563,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++) @@ -1568,7 +1584,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: head/sys/ufs/ffs/ffs_balloc.c ============================================================================== --- head/sys/ufs/ffs/ffs_balloc.c Wed Jul 17 21:25:26 2019 (r350095) +++ head/sys/ufs/ffs/ffs_balloc.c Wed Jul 17 22:07:43 2019 (r350096) @@ -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; @@ -113,6 +114,7 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, i 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; @@ -295,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; @@ -580,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; @@ -593,6 +601,7 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, i 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; @@ -888,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; Modified: head/sys/ufs/ffs/ffs_extern.h ============================================================================== --- head/sys/ufs/ffs/ffs_extern.h Wed Jul 17 21:25:26 2019 (r350095) +++ head/sys/ufs/ffs/ffs_extern.h Wed Jul 17 22:07:43 2019 (r350096) @@ -69,6 +69,7 @@ ufs2_daddr_t ffs_blkpref_ufs2(struct inode *, ufs_lbn_ void ffs_blkrelease_finish(struct ufsmount *, u_long); u_long ffs_blkrelease_start(struct ufsmount *, struct vnode *, ino_t); uint32_t ffs_calc_sbhash(struct fs *); +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: head/sys/ufs/ffs/ffs_softdep.c ============================================================================== --- head/sys/ufs/ffs/ffs_softdep.c Wed Jul 17 21:25:26 2019 (r350095) +++ head/sys/ufs/ffs/ffs_softdep.c Wed Jul 17 22:07:43 2019 (r350096) @@ -8124,6 +8124,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; @@ -8133,7 +8134,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: @@ -8226,6 +8228,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: head/sys/ufs/ffs/ffs_subr.c ============================================================================== --- head/sys/ufs/ffs/ffs_subr.c Wed Jul 17 21:25:26 2019 (r350095) +++ head/sys/ufs/ffs/ffs_subr.c Wed Jul 17 22:07:43 2019 (r350096) @@ -154,6 +154,55 @@ ffs_load_inode(struct buf *bp, struct inode *ip, struc ip->i_gid = dip2->di_gid; return (0); } + +/* + * 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 (EINTEGRITY); +} #endif /* _KERNEL */ /* Modified: head/sys/ufs/ffs/ffs_vfsops.c ============================================================================== --- head/sys/ufs/ffs/ffs_vfsops.c Wed Jul 17 21:25:26 2019 (r350095) +++ head/sys/ufs/ffs/ffs_vfsops.c Wed Jul 17 22:07:43 2019 (r350096) @@ -919,6 +919,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: head/sys/ufs/ufs/ufs_bmap.c ============================================================================== --- head/sys/ufs/ufs/ufs_bmap.c Wed Jul 17 21:25:26 2019 (r350095) +++ head/sys/ufs/ufs/ufs_bmap.c Wed Jul 17 22:07:43 2019 (r350096) @@ -267,8 +267,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 && @@ -287,7 +295,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: head/sys/ufs/ufs/ufsmount.h ============================================================================== --- head/sys/ufs/ufs/ufsmount.h Wed Jul 17 21:25:26 2019 (r350095) +++ head/sys/ufs/ufs/ufsmount.h Wed Jul 17 22:07:43 2019 (r350096) @@ -102,6 +102,8 @@ struct ufsmount { 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 */ @@ -121,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); }; /* @@ -145,6 +148,9 @@ struct ufsmount { #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)