⬢ DragonFlyBSD Kernel Audit
DF-5010 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/virtual/vkernel/disk/vdisk.c b/sys/dev/virtual/vkernel/disk/vdisk.c
index 4e7b3cdb..9b6a132a 100644
--- a/sys/dev/virtual/vkernel/disk/vdisk.c
+++ b/sys/dev/virtual/vkernel/disk/vdisk.c
@@ -305,6 +305,26 @@ vkd_doio(struct vkd_softc *sc, struct bio *bio)
 	struct buf *bp = bio->bio_buf;
 	int n;

+	/*
+	 * The slice layer (dscheck()) normally bounds bios against
+	 * d_media_blocks, but a driver must not blindly trust the
+	 * generic layer.  If an unbounded bio ever reaches this
+	 * function, the COW-mode memcpy below reads/writes the host
+	 * process mapping (sc->map_buf) out of bounds and the regular
+	 * mode pwrite() silently extends the backing file (sparse
+	 * growth).  Reject out-of-range I/O here as well.
+	 */
+	if (bp->b_cmd == BUF_CMD_READ || bp->b_cmd == BUF_CMD_WRITE) {
+		if (bio->bio_offset < 0 ||
+		    bio->bio_offset >= sc->size ||
+		    (off_t)bp->b_bcount > sc->size - bio->bio_offset) {
+			bp->b_error = EINVAL;
+			bp->b_flags |= B_ERROR | B_INVAL;
+			bp->b_resid = bp->b_bcount;
+			return;
+		}
+	}
+
 	switch(bp->b_cmd) {
 	case BUF_CMD_READ:
 		if (sc->map_buf) {
@@ -339,6 +359,8 @@ vkd_doio(struct vkd_softc *sc, struct bio *bio)
 		panic("vkd: bad b_cmd %d", bp->b_cmd);
 		break; /* not reached */
 	}
+	if (n < 0)
+		n = 0;		/* error: nothing was transferred */
 	if (bp->b_bcount != n) {
 		bp->b_error = EIO;
 		bp->b_flags |= B_ERROR;