6596419 zvol character (raw) devices allow read/write past the end of the device
authorgw25295
Wed, 02 Jul 2008 17:17:14 -0700
changeset 7013 540c400de3b4
parent 7012 932cd76b5633
child 7014 66db2660bf16
6596419 zvol character (raw) devices allow read/write past the end of the device Contributed by Juergen Keil <[email protected]>
usr/src/uts/common/fs/zfs/zvol.c
--- a/usr/src/uts/common/fs/zfs/zvol.c	Wed Jul 02 17:06:30 2008 -0700
+++ b/usr/src/uts/common/fs/zfs/zvol.c	Wed Jul 02 17:17:14 2008 -0700
@@ -1234,6 +1234,9 @@
 	addr = bp->b_un.b_addr;
 	resid = bp->b_bcount;
 
+	if (resid > 0 && (off < 0 || off >= volsize))
+		return (EIO);
+
 	/*
 	 * There must be no buffer changes when doing a dmu_sync() because
 	 * we can't change the data whilst calculating the checksum.
@@ -1345,6 +1348,7 @@
 {
 	minor_t minor = getminor(dev);
 	zvol_state_t *zv;
+	uint64_t volsize;
 	rl_t *rl;
 	int error = 0;
 
@@ -1355,11 +1359,20 @@
 	if (zv == NULL)
 		return (ENXIO);
 
+	volsize = zv->zv_volsize;
+	if (uio->uio_resid > 0 &&
+	    (uio->uio_loffset < 0 || uio->uio_loffset >= volsize))
+		return (EIO);
+
 	rl = zfs_range_lock(&zv->zv_znode, uio->uio_loffset, uio->uio_resid,
 	    RL_READER);
-	while (uio->uio_resid > 0) {
+	while (uio->uio_resid > 0 && uio->uio_loffset < volsize) {
 		uint64_t bytes = MIN(uio->uio_resid, DMU_MAX_ACCESS >> 1);
 
+		/* don't read past the end */
+		if (bytes > volsize - uio->uio_loffset)
+			bytes = volsize - uio->uio_loffset;
+
 		error =  dmu_read_uio(zv->zv_objset, ZVOL_OBJ, uio, bytes);
 		if (error)
 			break;
@@ -1374,6 +1387,7 @@
 {
 	minor_t minor = getminor(dev);
 	zvol_state_t *zv;
+	uint64_t volsize;
 	rl_t *rl;
 	int error = 0;
 
@@ -1384,6 +1398,11 @@
 	if (zv == NULL)
 		return (ENXIO);
 
+	volsize = zv->zv_volsize;
+	if (uio->uio_resid > 0 &&
+	    (uio->uio_loffset < 0 || uio->uio_loffset >= volsize))
+		return (EIO);
+
 	if (zv->zv_flags & ZVOL_DUMPIFIED) {
 		error = physio(zvol_strategy, NULL, dev, B_WRITE,
 		    zvol_minphys, uio);
@@ -1392,11 +1411,14 @@
 
 	rl = zfs_range_lock(&zv->zv_znode, uio->uio_loffset, uio->uio_resid,
 	    RL_WRITER);
-	while (uio->uio_resid > 0) {
+	while (uio->uio_resid > 0 && uio->uio_loffset < volsize) {
 		uint64_t bytes = MIN(uio->uio_resid, DMU_MAX_ACCESS >> 1);
 		uint64_t off = uio->uio_loffset;
+		dmu_tx_t *tx = dmu_tx_create(zv->zv_objset);
 
-		dmu_tx_t *tx = dmu_tx_create(zv->zv_objset);
+		if (bytes > volsize - off)	/* don't write past the end */
+			bytes = volsize - off;
+
 		dmu_tx_hold_write(tx, ZVOL_OBJ, off, bytes);
 		error = dmu_tx_assign(tx, TXG_WAIT);
 		if (error) {