svn commit: r339671 - in head: sbin/fsck_ffs sbin/newfs sys/sys sys/ufs/ffs

Kirk McKusick mckusick at FreeBSD.org
Tue Oct 23 21:10:08 UTC 2018


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 */ 


More information about the svn-src-all mailing list