Date: Tue, 23 Oct 2018 21:10:06 +0000 (UTC) From: Kirk McKusick <mckusick@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r339671 - in head: sbin/fsck_ffs sbin/newfs sys/sys sys/ufs/ffs Message-ID: <201810232110.w9NLA64C066948@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mckusick Date: Tue Oct 23 21:10:06 2018 New Revision: 339671 URL: https://svnweb.freebsd.org/changeset/base/339671 Log: Continuing efforts to provide hardening of FFS, this change adds a check hash to the superblock. If a check hash fails when an attempt is made to mount a filesystem, the mount fails with EINVAL (Invalid argument). This avoids a class of filesystem panics related to corrupted superblocks. The hash is done using crc32c. Check hases are added only to UFS2 and not to UFS1 as UFS1 is primarily used in embedded systems with small memories and low-powered processors which need as light-weight a filesystem as possible. Reviewed by: kib Tested by: Peter Holm Sponsored by: Netflix Modified: head/sbin/fsck_ffs/main.c head/sbin/newfs/mkfs.c head/sys/sys/param.h head/sys/ufs/ffs/ffs_subr.c head/sys/ufs/ffs/ffs_vfsops.c head/sys/ufs/ffs/fs.h Modified: head/sbin/fsck_ffs/main.c ============================================================================== --- head/sbin/fsck_ffs/main.c Tue Oct 23 21:09:37 2018 (r339670) +++ head/sbin/fsck_ffs/main.c Tue Oct 23 21:10:06 2018 (r339671) @@ -460,11 +460,13 @@ checkfilesys(char *filesys) if ((sblock.fs_metackhash & CK_CYLGRP) == 0 && reply("ADD CYLINDER GROUP CHECK-HASH PROTECTION") != 0) ckhashadd |= CK_CYLGRP; -#ifdef notyet if ((sblock.fs_metackhash & CK_SUPERBLOCK) == 0 && getosreldate() >= P_OSREL_CK_SUPERBLOCK && - reply("ADD SUPERBLOCK CHECK-HASH PROTECTION") != 0) - ckhashadd |= CK_SUPERBLOCK; + reply("ADD SUPERBLOCK CHECK-HASH PROTECTION") != 0) { + sblock.fs_metackhash |= CK_SUPERBLOCK; + sbdirty(); + } +#ifdef notyet if ((sblock.fs_metackhash & CK_INODE) == 0 && getosreldate() >= P_OSREL_CK_INODE && reply("ADD INODE CHECK-HASH PROTECTION") != 0) Modified: head/sbin/newfs/mkfs.c ============================================================================== --- head/sbin/newfs/mkfs.c Tue Oct 23 21:09:37 2018 (r339670) +++ head/sbin/newfs/mkfs.c Tue Oct 23 21:10:06 2018 (r339671) @@ -496,7 +496,9 @@ restart: if (Oflag > 1) { sblock.fs_flags |= FS_METACKHASH; if (getosreldate() >= P_OSREL_CK_CYLGRP) - sblock.fs_metackhash = CK_CYLGRP; + sblock.fs_metackhash |= CK_CYLGRP; + if (getosreldate() >= P_OSREL_CK_SUPERBLOCK) + sblock.fs_metackhash |= CK_SUPERBLOCK; } /* Modified: head/sys/sys/param.h ============================================================================== --- head/sys/sys/param.h Tue Oct 23 21:09:37 2018 (r339670) +++ head/sys/sys/param.h Tue Oct 23 21:10:06 2018 (r339671) @@ -88,6 +88,7 @@ #define P_OSREL_WRFSBASE 1200041 #define P_OSREL_CK_CYLGRP 1200046 #define P_OSREL_VMTOTAL64 1200054 +#define P_OSREL_CK_SUPERBLOCK 1300000 #define P_OSREL_MAJOR(x) ((x) / 100000) #endif Modified: head/sys/ufs/ffs/ffs_subr.c ============================================================================== --- head/sys/ufs/ffs/ffs_subr.c Tue Oct 23 21:09:37 2018 (r339670) +++ head/sys/ufs/ffs/ffs_subr.c Tue Oct 23 21:10:06 2018 (r339671) @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include <ufs/ufs/dinode.h> #include <ufs/ffs/fs.h> +uint32_t calculate_crc32c(uint32_t, const void *, size_t); struct malloc_type; #define UFS_MALLOC(size, type, flags) malloc(size) #define UFS_FREE(ptr, type) free(ptr) @@ -142,6 +143,7 @@ ffs_load_inode(struct buf *bp, struct inode *ip, struc static off_t sblock_try[] = SBLOCKSEARCH; static int readsuper(void *, struct fs **, off_t, int, int (*)(void *, off_t, void **, int)); +static uint32_t calc_sbhash(struct fs *); /* * Read a superblock from the devfd device. @@ -252,7 +254,8 @@ readsuper(void *devfd, struct fs **fsp, off_t sblocklo int (*readfunc)(void *devfd, off_t loc, void **bufp, int size)) { struct fs *fs; - int error; + int error, res; + uint32_t ckhash; error = (*readfunc)(devfd, sblockloc, (void **)fsp, SBLOCKSIZE); if (error != 0) @@ -267,7 +270,26 @@ readsuper(void *devfd, struct fs **fsp, off_t sblocklo fs->fs_ncg >= 1 && fs->fs_bsize >= MINBSIZE && fs->fs_bsize <= MAXBSIZE && - fs->fs_bsize >= roundup(sizeof(struct fs), DEV_BSIZE)) { + fs->fs_bsize >= roundup(sizeof(struct fs), DEV_BSIZE) && + fs->fs_sbsize <= SBLOCKSIZE) { + if (fs->fs_ckhash != (ckhash = calc_sbhash(fs))) { +#ifdef _KERNEL + res = uprintf("Superblock check-hash failed: recorded " + "check-hash 0x%x != computed check-hash 0x%x\n", + fs->fs_ckhash, ckhash); +#else + res = 0; +#endif + /* + * Print check-hash failure if no controlling terminal + * in kernel or always if in user-mode (libufs). + */ + if (res == 0) + printf("Superblock check-hash failed: recorded " + "check-hash 0x%x != computed check-hash " + "0x%x\n", fs->fs_ckhash, ckhash); + return (EINVAL); + } /* Have to set for old filesystems that predate this field */ fs->fs_sblockactualloc = sblockloc; /* Not yet any summary information */ @@ -313,9 +335,46 @@ ffs_sbput(void *devfd, struct fs *fs, off_t loc, } fs->fs_fmod = 0; fs->fs_time = UFS_TIME; + fs->fs_ckhash = calc_sbhash(fs); if ((error = (*writefunc)(devfd, loc, fs, fs->fs_sbsize)) != 0) return (error); return (0); +} + +/* + * Calculate the check-hash for a superblock. + */ +static uint32_t +calc_sbhash(struct fs *fs) +{ + uint32_t ckhash, save_ckhash; + + /* + * A filesystem that was using a superblock ckhash may be moved + * to an older kernel that does not support ckhashes. The + * older kernel will clear the FS_METACKHASH flag indicating + * that it does not update hashes. When the disk is moved back + * to a kernel capable of ckhashes it disables them on mount: + * + * if ((fs->fs_flags & FS_METACKHASH) == 0) + * fs->fs_metackhash = 0; + * + * This leaves (fs->fs_metackhash & CK_SUPERBLOCK) == 0) with an + * old stale value in the fs->fs_ckhash field. Thus the need to + * just accept what is there. + */ + if ((fs->fs_metackhash & CK_SUPERBLOCK) == 0) + return (fs->fs_ckhash); + + save_ckhash = fs->fs_ckhash; + fs->fs_ckhash = 0; + /* + * If newly read from disk, the caller is responsible for + * verifying that fs->fs_sbsize <= SBLOCKSIZE. + */ + ckhash = calculate_crc32c(~0L, (void *)fs, fs->fs_sbsize); + fs->fs_ckhash = save_ckhash; + return (ckhash); } /* Modified: head/sys/ufs/ffs/ffs_vfsops.c ============================================================================== --- head/sys/ufs/ffs/ffs_vfsops.c Tue Oct 23 21:09:37 2018 (r339670) +++ head/sys/ufs/ffs/ffs_vfsops.c Tue Oct 23 21:10:06 2018 (r339671) @@ -814,7 +814,7 @@ ffs_mountfs(devvp, mp, td) if ((fs->fs_flags & FS_METACKHASH) == 0) fs->fs_metackhash = 0; /* none of these types of check-hashes are maintained by this kernel */ - fs->fs_metackhash &= ~(CK_SUPERBLOCK | CK_INODE | CK_INDIR | CK_DIR); + fs->fs_metackhash &= ~(CK_INODE | CK_INDIR | CK_DIR); /* no support for any undefined flags */ fs->fs_flags &= FS_SUPPORTED; fs->fs_flags &= ~FS_UNCLEAN; Modified: head/sys/ufs/ffs/fs.h ============================================================================== --- head/sys/ufs/ffs/fs.h Tue Oct 23 21:09:37 2018 (r339670) +++ head/sys/ufs/ffs/fs.h Tue Oct 23 21:10:06 2018 (r339671) @@ -364,7 +364,8 @@ struct fs { int32_t fs_save_cgsize; /* save real cg size to use fs_bsize */ ufs_time_t fs_mtime; /* Last mount or fsck time. */ int32_t fs_sujfree; /* SUJ free list */ - int32_t fs_sparecon32[22]; /* reserved for future constants */ + int32_t fs_sparecon32[21]; /* reserved for future constants */ + u_int32_t fs_ckhash; /* if CK_SUPERBLOCK, its check-hash */ u_int32_t fs_metackhash; /* metadata check-hash, see CK_ below */ int32_t fs_flags; /* see FS_ flags below */ int32_t fs_contigsumsize; /* size of cluster summary array */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201810232110.w9NLA64C066948>