⬢ DragonFlyBSD Kernel Audit
DF-3152 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/hammer/hammer_ondisk.c
+++ b/sys/vfs/hammer/hammer_ondisk.c
@@ -119,6 +119,7 @@
 	int ronly;
 	int setmp = 0;
 	int i;
+	struct partinfo dpart;
 
 	mp = hmp->mp;
 	ronly = ((mp->mnt_flag & MNT_RDONLY) ? 1 : 0);
@@ -174,6 +175,31 @@
 	setmp = 1;
 
 	/*
+	 * When formatting a new volume (hammer volume-add) the caller
+	 * supplies the intended volume header.  vol_buf_end derives from
+	 * the user-supplied vol_size and must not exceed the real size of
+	 * the device, otherwise freemap formatting walks past the device
+	 * end (panic on INVARIANTS kernels, half-formatted volume plus a
+	 * swallowed error on production kernels).  HAMMER2 already does
+	 * the equivalent check in its own ondisk.c.
+	 */
+	if (data) {
+		if (VOP_IOCTL(volume->devvp, DIOCGPART, (void *)&dpart,
+			      0, FSCRED, NULL) == 0 &&
+		    ((hammer_volume_ondisk_t)data)->vol_buf_end >
+		    (int64_t)dpart.media_size) {
+			hkprintf("volume %s: vol_buf_end %jd exceeds device "
+				 "size %ju\n",
+				 volname,
+				 (intmax_t)((hammer_volume_ondisk_t)data)->
+				 vol_buf_end,
+				 (uintmax_t)dpart.media_size);
+			error = EFTYPE;
+			goto late_failure;
+		}
+	}
+
+	/*
 	 * Extract the volume number from the volume header and do various
 	 * sanity checks.
 	 */
--- a/sys/vfs/hammer/hammer_volume.c
+++ b/sys/vfs/hammer/hammer_volume.c
@@ -115,12 +115,17 @@
 	KKASSERT(volume != NULL && error == 0);
 
 	error =	hammer_format_freemap(trans, volume);
-	KKASSERT(error == 0);
+	if (error)
+		goto add_failed;
 
 	error = hammer_count_bigblocks(hmp, volume,
 			&total_bigblocks, &empty_bigblocks);
-	KKASSERT(error == 0);
-	KKASSERT(total_bigblocks == empty_bigblocks);
+	if (error)
+		goto add_failed;
+	if (total_bigblocks != empty_bigblocks) {
+		error = EFTYPE;
+		goto add_failed;
+	}
 
 	hammer_rel_volume(volume, 0);
 
@@ -138,6 +143,20 @@
 	if (error)
 		hmkprintf(hmp, "An error occurred: %d\n", error);
 	return (error);
+
+add_failed:
+	/*
+	 * Freemap formatting or counting failed (device I/O error, or a
+	 * claimed vol_size larger than the real device).  Uninstall the
+	 * volume we just installed so the filesystem is not left with a
+	 * half-formatted member, and return the error.  Never report
+	 * success on this path.
+	 */
+	hammer_rel_volume(volume, 0);
+	hammer_unload_volume(volume, NULL);
+	hammer_unlock(&hmp->blkmap_lock);
+	hammer_sync_unlock(trans);
+	goto end;
 }