untrusted comment: verify with openbsd-68-base.pub
RWQZj25CSG5R2vcQVSpjuOG3lYIbuZWDGkvuty/hqr+WKQaGcV0GT2QDBH8MAIikXhkmnBMamr0tjq8KwoNM37r8Ac0alaUOeAg=

OpenBSD 6.8 errata 020, May 18, 2021:

vmd guest virtio drivers could cause stack overflows by crafting
invalid virtio descriptor lengths.

Apply by doing:
    signify -Vep /etc/signify/openbsd-68-base.pub -x 020_vmd.patch.sig \
        -m - | (cd /usr/src && patch -p0)

And then rebuild and install vmd(8) and vmctl(8):
    cd /usr/src/usr.sbin/vmd
    make obj
    make
    make install
    cd /usr/src/usr.sbin/vmctl
    make obj
    make
    make install

Index: usr.sbin/vmd/vioscsi.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vioscsi.c,v
diff -u -p -r1.14 vioscsi.c
--- usr.sbin/vmd/vioscsi.c	3 Sep 2020 13:11:49 -0000	1.14
+++ usr.sbin/vmd/vioscsi.c	13 May 2021 18:56:16 -0000
@@ -186,10 +186,16 @@ vioscsi_free_info(struct ioinfo *info)
 }
 
 static struct ioinfo *
-vioscsi_start_read(struct vioscsi_dev *dev, off_t block, ssize_t n_blocks)
+vioscsi_start_read(struct vioscsi_dev *dev, off_t block, size_t n_blocks)
 {
 	struct ioinfo *info;
 
+	/* Limit to 64M for now */
+	if (n_blocks * VIOSCSI_BLOCK_SIZE_CDROM > (1 << 26)) {
+		log_warnx("%s: read size exceeded 64M", __func__);
+		return (NULL);
+	}
+
 	info = calloc(1, sizeof(*info));
 	if (!info)
 		goto nomem;
@@ -237,7 +243,7 @@ vioscsi_handle_tur(struct vioscsi_dev *d
 
 	vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 	} else {
@@ -295,7 +301,7 @@ vioscsi_handle_inquiry(struct vioscsi_de
 	    "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
 	    acct->resp_desc->len, acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 		goto free_inq;
@@ -310,7 +316,8 @@ vioscsi_handle_inquiry(struct vioscsi_de
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, inq_data, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, inq_data,
+		sizeof(struct scsi_inquiry_data))) {
 		log_warnx("%s: unable to write inquiry"
 		    " response to gpa @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
@@ -337,7 +344,7 @@ vioscsi_handle_mode_sense(struct vioscsi
 	uint8_t mode_page_ctl;
 	uint8_t mode_page_code;
 	uint8_t *mode_reply;
-	uint8_t mode_reply_len;
+	uint8_t mode_reply_len = 0;
 	struct scsi_mode_sense *mode_sense;
 
 	memset(&resp, 0, sizeof(resp));
@@ -356,7 +363,7 @@ vioscsi_handle_mode_sense(struct vioscsi
 		 * mode sense header is 4 bytes followed
 		 * by a variable page
 		 * ERR_RECOVERY_PAGE is 12 bytes
-		 * CDVD_CAPABILITIES_PAGE is 27 bytes
+		 * CDVD_CAPABILITIES_PAGE is 32 bytes
 		 */
 		switch (mode_page_code) {
 		case ERR_RECOVERY_PAGE:
@@ -376,7 +383,7 @@ vioscsi_handle_mode_sense(struct vioscsi
 			*(mode_reply + 7) = MODE_READ_RETRY_COUNT;
 			break;
 		case CDVD_CAPABILITIES_PAGE:
-			mode_reply_len = 31;
+			mode_reply_len = 36;
 			mode_reply =
 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
 			if (mode_reply == NULL)
@@ -403,11 +410,10 @@ vioscsi_handle_mode_sense(struct vioscsi
 
 		DPRINTF("%s: writing resp to 0x%llx size %d "
 		    "at local idx %d req_idx %d global_idx %d",
-		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
+		    __func__, acct->resp_desc->addr, mode_reply_len,
 		    acct->resp_idx, acct->req_idx, acct->idx);
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to write OK"
 			    " resp status data @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
@@ -421,12 +427,11 @@ vioscsi_handle_mode_sense(struct vioscsi
 
 		DPRINTF("%s: writing mode_reply to 0x%llx "
 		    "size %d at local idx %d req_idx %d "
-		    "global_idx %d",__func__, acct->resp_desc->addr,
-		    acct->resp_desc->len, acct->resp_idx, acct->req_idx,
-		    acct->idx);
+		    "global_idx %d", __func__, acct->resp_desc->addr,
+		    mode_reply_len, acct->resp_idx, acct->req_idx, acct->idx);
 
 		if (write_mem(acct->resp_desc->addr, mode_reply,
-		    acct->resp_desc->len)) {
+			mode_reply_len)) {
 			log_warnx("%s: unable to write "
 			    "mode_reply to gpa @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
@@ -452,8 +457,7 @@ mode_sense_error:
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
 			goto mode_sense_out;
@@ -478,7 +482,7 @@ vioscsi_handle_mode_sense_big(struct vio
 	uint8_t mode_page_ctl;
 	uint8_t mode_page_code;
 	uint8_t *mode_reply;
-	uint8_t mode_reply_len;
+	uint8_t mode_reply_len = 0;
 	uint16_t mode_sense_len;
 	struct scsi_mode_sense_big *mode_sense_10;
 
@@ -499,7 +503,7 @@ vioscsi_handle_mode_sense_big(struct vio
 		 * mode sense header is 8 bytes followed
 		 * by a variable page
 		 * ERR_RECOVERY_PAGE is 12 bytes
-		 * CDVD_CAPABILITIES_PAGE is 27 bytes
+		 * CDVD_CAPABILITIES_PAGE is 32 bytes
 		 */
 		switch (mode_page_code) {
 		case ERR_RECOVERY_PAGE:
@@ -519,7 +523,7 @@ vioscsi_handle_mode_sense_big(struct vio
 			*(mode_reply + 11) = MODE_READ_RETRY_COUNT;
 			break;
 		case CDVD_CAPABILITIES_PAGE:
-			mode_reply_len = 35;
+			mode_reply_len = 40;
 			mode_reply =
 			    (uint8_t*)calloc(mode_reply_len, sizeof(uint8_t));
 			if (mode_reply == NULL)
@@ -549,8 +553,7 @@ vioscsi_handle_mode_sense_big(struct vio
 		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 		    acct->resp_idx, acct->req_idx, acct->idx);
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to write OK"
 			    " resp status data @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
@@ -564,11 +567,11 @@ vioscsi_handle_mode_sense_big(struct vio
 
 		DPRINTF("%s: writing mode_reply to 0x%llx "
 		    "size %d at local idx %d req_idx %d global_idx %d",
-		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
+		    __func__, acct->resp_desc->addr, mode_reply_len,
 		    acct->resp_idx, acct->req_idx, acct->idx);
 
 		if (write_mem(acct->resp_desc->addr, mode_reply,
-		    acct->resp_desc->len)) {
+			mode_reply_len)) {
 			log_warnx("%s: unable to write "
 			    "mode_reply to gpa @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
@@ -594,8 +597,7 @@ mode_sense_big_error:
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
 			goto mode_sense_big_out;
@@ -666,7 +668,7 @@ vioscsi_handle_read_capacity(struct vios
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 		goto free_read_capacity;
@@ -682,7 +684,7 @@ vioscsi_handle_read_capacity(struct vios
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
 	if (write_mem(acct->resp_desc->addr, r_cap_data,
-	    acct->resp_desc->len)) {
+		sizeof(struct scsi_read_cap_data))) {
 		log_warnx("%s: unable to write read_cap_data"
 		    " response to gpa @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
@@ -741,7 +743,7 @@ vioscsi_handle_read_capacity_16(struct v
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status "
 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
 		goto free_read_capacity_16;
@@ -757,7 +759,7 @@ vioscsi_handle_read_capacity_16(struct v
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
 	if (write_mem(acct->resp_desc->addr, r_cap_data_16,
-	    acct->resp_desc->len)) {
+		sizeof(struct scsi_read_cap_data_16))) {
 		log_warnx("%s: unable to write read_cap_data_16"
 		    " response to gpa @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
@@ -804,8 +806,7 @@ vioscsi_handle_report_luns(struct vioscs
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR "
 			    "status data @ 0x%llx", __func__,
 			    acct->resp_desc->addr);
@@ -820,7 +821,7 @@ vioscsi_handle_report_luns(struct vioscs
 
 	}
 
-	reply_rpl = calloc(1, sizeof(*reply_rpl));
+	reply_rpl = calloc(1, sizeof(struct vioscsi_report_luns_data));
 
 	if (reply_rpl == NULL) {
 		log_warnx("%s: cannot alloc reply_rpl", __func__);
@@ -841,7 +842,7 @@ vioscsi_handle_report_luns(struct vioscs
 	    "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
 	    acct->resp_desc->len, acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 		goto free_rpl;
@@ -856,7 +857,8 @@ vioscsi_handle_report_luns(struct vioscs
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, reply_rpl, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, reply_rpl,
+		sizeof(struct vioscsi_report_luns_data))) {
 		log_warnx("%s: unable to write reply_rpl"
 		    " response to gpa @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
@@ -906,8 +908,7 @@ vioscsi_handle_read_6(struct vioscsi_dev
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR "
 			    "status data @ 0x%llx", __func__,
 			    acct->resp_desc->addr);
@@ -942,8 +943,7 @@ vioscsi_handle_read_6(struct vioscsi_dev
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR "
 			    "status data @ 0x%llx", __func__,
 			    acct->resp_desc->addr);
@@ -969,7 +969,7 @@ vioscsi_handle_read_6(struct vioscsi_dev
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status "
 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
 		goto free_read_6;
@@ -984,8 +984,7 @@ vioscsi_handle_read_6(struct vioscsi_dev
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, read_buf,
-	    acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, read_buf, info->len)) {
 		log_warnx("%s: unable to write read_buf to gpa @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 	} else {
@@ -1014,6 +1013,7 @@ vioscsi_handle_read_10(struct vioscsi_de
 	off_t chunk_offset;
 	struct ioinfo *info;
 	struct scsi_rw_10 *read_10;
+	size_t chunk_len = 0;
 
 	memset(&resp, 0, sizeof(resp));
 	read_10 = (struct scsi_rw_10 *)(req->cdb);
@@ -1037,8 +1037,7 @@ vioscsi_handle_read_10(struct vioscsi_de
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
 		} else {
@@ -1072,8 +1071,7 @@ vioscsi_handle_read_10(struct vioscsi_de
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
 		} else {
@@ -1098,7 +1096,7 @@ vioscsi_handle_read_10(struct vioscsi_de
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status "
 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
 		goto free_read_10;
@@ -1120,8 +1118,16 @@ vioscsi_handle_read_10(struct vioscsi_de
 		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 		    acct->resp_idx, acct->req_idx, acct->idx);
 
-		if (write_mem(acct->resp_desc->addr,
-		    read_buf + chunk_offset, acct->resp_desc->len)) {
+		/* Check we don't read beyond read_buf boundaries. */
+		if (acct->resp_desc->len > info->len - chunk_offset) {
+			log_warnx("%s: descriptor length beyond read_buf len",
+			    __func__);
+			chunk_len = info->len - chunk_offset;
+		} else
+			chunk_len = acct->resp_desc->len;
+
+		if (write_mem(acct->resp_desc->addr, read_buf + chunk_offset,
+			chunk_len)) {
 			log_warnx("%s: unable to write read_buf"
 			    " to gpa @ 0x%llx", __func__,
 			    acct->resp_desc->addr);
@@ -1164,7 +1170,7 @@ vioscsi_handle_prevent_allow(struct vios
 
 	dev->locked = dev->locked ? 0 : 1;
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 	} else {
@@ -1193,7 +1199,8 @@ vioscsi_handle_mechanism_status(struct v
 	mech_status_len = (uint16_t)_2btol(mech_status->length);
 	DPRINTF("%s: MECH_STATUS Len %u", __func__, mech_status_len);
 
-	mech_status_header = calloc(1, sizeof(*mech_status_header));
+	mech_status_header = calloc(1,
+	    sizeof(struct scsi_mechanism_status_header));
 
 	if (mech_status_header == NULL)
 		goto mech_out;
@@ -1206,7 +1213,7 @@ vioscsi_handle_mechanism_status(struct v
 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 	    acct->req_desc, &(acct->resp_idx));
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to set ERR status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 		goto free_mech;
@@ -1217,7 +1224,7 @@ vioscsi_handle_mechanism_status(struct v
 	    &(acct->resp_idx));
 
 	if (write_mem(acct->resp_desc->addr, mech_status_header,
-	    acct->resp_desc->len)) {
+		sizeof(struct scsi_mechanism_status_header))) {
 		log_warnx("%s: unable to write "
 		    "mech_status_header response to "
 		    "gpa @ 0x%llx",
@@ -1272,8 +1279,7 @@ vioscsi_handle_read_toc(struct vioscsi_d
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR status data @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
 			goto read_toc_out;
@@ -1347,7 +1353,7 @@ vioscsi_handle_read_toc(struct vioscsi_d
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 		goto read_toc_out;
@@ -1362,8 +1368,7 @@ vioscsi_handle_read_toc(struct vioscsi_d
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, toc_data,
-	    acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, toc_data, sizeof(toc_data))) {
 		log_warnx("%s: unable to write toc descriptor data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 	} else {
@@ -1400,8 +1405,7 @@ vioscsi_handle_read_disc_info(struct vio
 	acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 	    acct->req_desc, &(acct->resp_idx));
 
-	if (write_mem(acct->resp_desc->addr, &resp,
-	    acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to set ERR status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 	} else {
@@ -1441,8 +1445,7 @@ vioscsi_handle_gesn(struct vioscsi_dev *
 		acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
 		    acct->req_desc, &(acct->resp_idx));
 
-		if (write_mem(acct->resp_desc->addr, &resp,
-		    acct->resp_desc->len)) {
+		if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 			log_warnx("%s: unable to set ERR status  data @ 0x%llx",
 			    __func__, acct->resp_desc->addr);
 			goto gesn_out;
@@ -1480,7 +1483,7 @@ vioscsi_handle_gesn(struct vioscsi_dev *
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp, acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to write OK resp status "
 		    "data @ 0x%llx", __func__, acct->resp_desc->addr);
 		goto gesn_out;
@@ -1495,8 +1498,7 @@ vioscsi_handle_gesn(struct vioscsi_dev *
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, gesn_reply,
-	    acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, gesn_reply, sizeof(gesn_reply))) {
 		log_warnx("%s: unable to write gesn_reply"
 		    " response to gpa @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
@@ -1625,8 +1627,7 @@ vioscsi_handle_get_config(struct vioscsi
 	    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
-	if (write_mem(acct->resp_desc->addr, &resp,
-	    acct->resp_desc->len)) {
+	if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
 		log_warnx("%s: unable to set Ok status data @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
 		goto free_get_config;
@@ -1642,7 +1643,7 @@ vioscsi_handle_get_config(struct vioscsi
 	    acct->resp_idx, acct->req_idx, acct->idx);
 
 	if (write_mem(acct->resp_desc->addr, get_conf_reply,
-	    acct->resp_desc->len)) {
+		G_CONFIG_REPLY_SIZE)) {
 		log_warnx("%s: unable to write get_conf_reply"
 		    " response to gpa @ 0x%llx",
 		    __func__, acct->resp_desc->addr);
@@ -2081,7 +2082,7 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
 {
 	uint64_t q_gpa;
 	uint32_t vr_sz;
-	int ret;
+	int cnt, ret;
 	char *vr;
 	struct virtio_scsi_req_hdr req;
 	struct virtio_scsi_res_hdr resp;
@@ -2123,8 +2124,15 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
 		goto out;
 	}
 
+	cnt = 0;
 	while (acct.idx != (acct.avail->idx & VIOSCSI_QUEUE_MASK)) {
 
+		/* Guard against infinite descriptor chains */
+		if (++cnt >= VIOSCSI_QUEUE_SIZE) {
+			log_warnx("%s: invalid descriptor table", __func__);
+			goto out;
+		}
+
 		acct.req_idx = acct.avail->ring[acct.idx] & VIOSCSI_QUEUE_MASK;
 		acct.req_desc = &(acct.desc[acct.req_idx]);
 
@@ -2138,7 +2146,7 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
 		}
 
 		/* Read command from descriptor ring */
-		if (read_mem(acct.req_desc->addr, &req, acct.req_desc->len)) {
+		if (read_mem(acct.req_desc->addr, &req, sizeof(req))) {
 			log_warnx("%s: command read_mem error @ 0x%llx",
 			    __func__, acct.req_desc->addr);
 			goto out;
@@ -2170,8 +2178,13 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
 			vioscsi_prepare_resp(&resp,
 			    VIRTIO_SCSI_S_BAD_TARGET, SCSI_OK, 0, 0, 0);
 
+			if (acct.resp_desc->len > sizeof(resp)) {
+				log_warnx("%s: invalid descriptor length",
+				    __func__);
+				goto out;
+			}
 			if (write_mem(acct.resp_desc->addr, &resp,
-			    acct.resp_desc->len)) {
+				sizeof(resp))) {
 				log_warnx("%s: unable to write BAD_TARGET"
 				    " resp status data @ 0x%llx",
 				    __func__, acct.resp_desc->addr);
Index: usr.sbin/vmd/virtio.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.c,v
diff -u -p -r1.82 virtio.c
--- usr.sbin/vmd/virtio.c	11 Dec 2019 06:45:16 -0000	1.82
+++ usr.sbin/vmd/virtio.c	13 May 2021 18:56:16 -0000
@@ -30,6 +30,7 @@
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
+#include <netinet/ip.h>
 
 #include <errno.h>
 #include <event.h>
@@ -86,6 +87,8 @@ vioblk_cmd_name(uint32_t type)
 static void
 dump_descriptor_chain(struct vring_desc *desc, int16_t dxx)
 {
+	unsigned int cnt = 0;
+
 	log_debug("descriptor chain @ %d", dxx);
 	do {
 		log_debug("desc @%d addr/len/flags/next = 0x%llx / 0x%x "
@@ -96,6 +99,15 @@ dump_descriptor_chain(struct vring_desc 
 		    desc[dxx].flags,
 		    desc[dxx].next);
 		dxx = desc[dxx].next;
+
+		/*
+		 * Dump up to the max number of descriptor for the largest
+		 * queue we support, which currently is VIONET_QUEUE_SIZE.
+		 */
+		if (++cnt >= VIONET_QUEUE_SIZE) {
+			log_warnx("%s: descriptor table invalid", __func__);
+			return;
+		}
 	} while (desc[dxx].flags & VRING_DESC_F_NEXT);
 
 	log_debug("desc @%d addr/len/flags/next = 0x%llx / 0x%x / 0x%x "
@@ -220,8 +232,7 @@ viornd_notifyq(void)
 
 	if (rnd_data != NULL) {
 		arc4random_buf(rnd_data, desc[avail->ring[aidx]].len);
-		if (write_mem(desc[avail->ring[aidx]].addr,
-		    rnd_data, desc[avail->ring[aidx]].len)) {
+		if (write_mem(desc[avail->ring[aidx]].addr, rnd_data, sz)) {
 			log_warnx("viornd: can't write random data @ "
 			    "0x%llx",
 			    desc[avail->ring[aidx]].addr);
@@ -349,10 +360,16 @@ vioblk_free_info(struct ioinfo *info)
 }
 
 static struct ioinfo *
-vioblk_start_read(struct vioblk_dev *dev, off_t sector, ssize_t sz)
+vioblk_start_read(struct vioblk_dev *dev, off_t sector, size_t sz)
 {
 	struct ioinfo *info;
 
+	/* Limit to 64M for now */
+	if (sz > (1 << 26)) {
+		log_warnx("%s: read size exceeded 64M", __func__);
+		return (NULL);
+	}
+
 	info = calloc(1, sizeof(*info));
 	if (!info)
 		goto nomem;
@@ -393,9 +410,16 @@ vioblk_start_write(struct vioblk_dev *de
 {
 	struct ioinfo *info;
 
+	/* Limit to 64M for now */
+	if (len > (1 << 26)) {
+		log_warnx("%s: write size exceeded 64M", __func__);
+		return (NULL);
+	}
+
 	info = calloc(1, sizeof(*info));
 	if (!info)
 		goto nomem;
+
 	info->buf = malloc(len);
 	if (info->buf == NULL)
 		goto nomem;
@@ -403,7 +427,7 @@ vioblk_start_write(struct vioblk_dev *de
 	info->offset = sector * VIRTIO_BLK_SECTOR_SIZE;
 	info->file = &dev->file;
 
-	if (read_mem(addr, info->buf, len)) {
+	if (read_mem(addr, info->buf, info->len)) {
 		vioblk_free_info(info);
 		return NULL;
 	}
@@ -440,7 +463,7 @@ vioblk_notifyq(struct vioblk_dev *dev)
 	uint32_t vr_sz;
 	uint16_t idx, cmd_desc_idx, secdata_desc_idx, ds_desc_idx;
 	uint8_t ds;
-	int ret;
+	int cnt, ret;
 	off_t secbias;
 	char *vr;
 	struct vring_desc *desc, *cmd_desc, *secdata_desc, *ds_desc;
@@ -495,7 +518,7 @@ vioblk_notifyq(struct vioblk_dev *dev)
 		}
 
 		/* Read command from descriptor ring */
-		if (read_mem(cmd_desc->addr, &cmd, cmd_desc->len)) {
+		if (read_mem(cmd_desc->addr, &cmd, sizeof(cmd))) {
 			log_warnx("vioblk: command read_mem error @ 0x%llx",
 			    cmd_desc->addr);
 			goto out;
@@ -513,14 +536,14 @@ vioblk_notifyq(struct vioblk_dev *dev)
 				goto out;
 			}
 
+			cnt = 0;
 			secbias = 0;
 			do {
 				struct ioinfo *info;
 				const uint8_t *secdata;
 
 				info = vioblk_start_read(dev,
-				    cmd.sector + secbias,
-				    (ssize_t)secdata_desc->len);
+				    cmd.sector + secbias, secdata_desc->len);
 
 				/* read the data, use current data descriptor */
 				secdata = vioblk_finish_read(info);
@@ -532,7 +555,7 @@ vioblk_notifyq(struct vioblk_dev *dev)
 				}
 
 				if (write_mem(secdata_desc->addr, secdata,
-				    secdata_desc->len)) {
+					secdata_desc->len)) {
 					log_warnx("can't write sector "
 					    "data to gpa @ 0x%llx",
 					    secdata_desc->addr);
@@ -549,13 +572,20 @@ vioblk_notifyq(struct vioblk_dev *dev)
 				secdata_desc_idx = secdata_desc->next &
 				    VIOBLK_QUEUE_MASK;
 				secdata_desc = &desc[secdata_desc_idx];
+
+				/* Guard against infinite chains */
+				if (++cnt >= VIOBLK_QUEUE_SIZE) {
+					log_warnx("%s: descriptor table "
+					    "invalid", __func__);
+					goto out;
+				}
 			} while (secdata_desc->flags & VRING_DESC_F_NEXT);
 
 			ds_desc_idx = secdata_desc_idx;
 			ds_desc = secdata_desc;
 
 			ds = VIRTIO_BLK_S_OK;
-			if (write_mem(ds_desc->addr, &ds, ds_desc->len)) {
+			if (write_mem(ds_desc->addr, &ds, sizeof(ds))) {
 				log_warnx("can't write device status data @ "
 				    "0x%llx", ds_desc->addr);
 				dump_descriptor_chain(desc, cmd_desc_idx);
@@ -594,6 +624,7 @@ vioblk_notifyq(struct vioblk_dev *dev)
 				goto out;
 			}
 
+			cnt = 0;
 			secbias = 0;
 			do {
 				struct ioinfo *info;
@@ -626,13 +657,20 @@ vioblk_notifyq(struct vioblk_dev *dev)
 				secdata_desc_idx = secdata_desc->next &
 				    VIOBLK_QUEUE_MASK;
 				secdata_desc = &desc[secdata_desc_idx];
+
+				/* Guard against infinite chains */
+				if (++cnt >= VIOBLK_QUEUE_SIZE) {
+					log_warnx("%s: descriptor table "
+					    "invalid", __func__);
+					goto out;
+				}
 			} while (secdata_desc->flags & VRING_DESC_F_NEXT);
 
 			ds_desc_idx = secdata_desc_idx;
 			ds_desc = secdata_desc;
 
 			ds = VIRTIO_BLK_S_OK;
-			if (write_mem(ds_desc->addr, &ds, ds_desc->len)) {
+			if (write_mem(ds_desc->addr, &ds, sizeof(ds))) {
 				log_warnx("wr vioblk: can't write device "
 				    "status data @ 0x%llx", ds_desc->addr);
 				dump_descriptor_chain(desc, cmd_desc_idx);
@@ -658,7 +696,7 @@ vioblk_notifyq(struct vioblk_dev *dev)
 			ds_desc = &desc[ds_desc_idx];
 
 			ds = VIRTIO_BLK_S_OK;
-			if (write_mem(ds_desc->addr, &ds, ds_desc->len)) {
+			if (write_mem(ds_desc->addr, &ds, sizeof(ds))) {
 				log_warnx("fl vioblk: "
 				    "can't write device status "
 				    "data @ 0x%llx", ds_desc->addr);
@@ -1075,7 +1113,7 @@ vionet_update_qs(struct vionet_dev *dev)
  * Must be called with dev->mutex acquired.
  */
 int
-vionet_enq_rx(struct vionet_dev *dev, char *pkt, ssize_t sz, int *spc)
+vionet_enq_rx(struct vionet_dev *dev, char *pkt, size_t sz, int *spc)
 {
 	uint64_t q_gpa;
 	uint32_t vr_sz;
@@ -1083,7 +1121,7 @@ vionet_enq_rx(struct vionet_dev *dev, ch
 	ptrdiff_t off;
 	int ret;
 	char *vr;
-	ssize_t rem;
+	size_t rem;
 	struct vring_desc *desc, *pkt_desc, *hdr_desc;
 	struct vring_avail *avail;
 	struct vring_used *used;
@@ -1092,6 +1130,11 @@ vionet_enq_rx(struct vionet_dev *dev, ch
 
 	ret = 0;
 
+	if (sz < 1 || sz > IP_MAXPACKET + ETHER_HDR_LEN) {
+		log_warn("%s: invalid packet size", __func__);
+		return (0);
+	}
+
 	if (!(dev->cfg.device_status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK))
 		return ret;
 
@@ -1155,14 +1198,14 @@ vionet_enq_rx(struct vionet_dev *dev, ch
 
 	if (rem >= sz) {
 		if (write_mem(hdr_desc->addr + sizeof(struct virtio_net_hdr),
-		    pkt, sz)) {
+			pkt, sz)) {
 			log_warnx("vionet: rx enq packet write_mem error @ "
 			    "0x%llx", pkt_desc->addr);
 			goto out;
 		}
 	} else {
 		/* Fallback to pkt_desc descriptor */
-		if ((uint64_t)pkt_desc->len >= (uint64_t)sz) {
+		if (pkt_desc->len >= sz) {
 			/* Must be not readable */
 			if ((pkt_desc->flags & VRING_DESC_F_WRITE) == 0) {
 				log_warnx("unexpected readable rx desc %d",
@@ -1231,7 +1274,7 @@ vionet_rx(struct vionet_dev *dev)
 			if (errno != EAGAIN)
 				log_warn("unexpected read error on vionet "
 				    "device");
-		} else if (sz != 0) {
+		} else if (sz > 0) {
 			eh = (struct ether_header *)buf;
 			if (!dev->lockedmac || sz < ETHER_HDR_LEN ||
 			    ETHER_IS_MULTICAST(eh->ether_dhost) ||
@@ -1392,8 +1435,8 @@ vionet_notify_tx(struct vionet_dev *dev)
 {
 	uint64_t q_gpa;
 	uint32_t vr_sz;
-	uint16_t idx, pkt_desc_idx, hdr_desc_idx, dxx;
-	size_t pktsz;
+	uint16_t idx, pkt_desc_idx, hdr_desc_idx, dxx, cnt;
+	size_t pktsz, chunk_size = 0;
 	ssize_t dhcpsz;
 	int ret, num_enq, ofs, spc;
 	char *vr, *pkt, *dhcppkt;
@@ -1440,10 +1483,23 @@ vionet_notify_tx(struct vionet_dev *dev)
 		hdr_desc = &desc[hdr_desc_idx];
 		pktsz = 0;
 
+		cnt = 0;
 		dxx = hdr_desc_idx;
 		do {
 			pktsz += desc[dxx].len;
 			dxx = desc[dxx].next;
+
+			/*
+			 * Virtio 1.0, cs04, section 2.4.5:
+			 *  "The number of descriptors in the table is defined
+			 *   by the queue size for this virtqueue: this is the
+			 *   maximum possible descriptor chain length."
+			 */
+			if (++cnt >= VIONET_QUEUE_SIZE) {
+				log_warnx("%s: descriptor table invalid",
+				    __func__);
+				goto out;
+			}
 		} while (desc[dxx].flags & VRING_DESC_F_NEXT);
 
 		pktsz += desc[dxx].len;
@@ -1451,11 +1507,12 @@ vionet_notify_tx(struct vionet_dev *dev)
 		/* Remove virtio header descriptor len */
 		pktsz -= hdr_desc->len;
 
-		/*
-		 * XXX check sanity pktsz
-		 * XXX too long and  > PAGE_SIZE checks
-		 *     (PAGE_SIZE can be relaxed to 16384 later)
-		 */
+		/* Only allow buffer len < max IP packet + Ethernet header */
+		if (pktsz > IP_MAXPACKET + ETHER_HDR_LEN) {
+			log_warnx("%s: invalid packet size %lu", __func__,
+			    pktsz);
+			goto out;
+		}
 		pkt = malloc(pktsz);
 		if (pkt == NULL) {
 			log_warn("malloc error alloc packet buf");
@@ -1474,9 +1531,16 @@ vionet_notify_tx(struct vionet_dev *dev)
 				goto out;
 			}
 
+			/* Check we don't read beyond allocated pktsz */
+			if (pkt_desc->len > pktsz - ofs) {
+				log_warnx("%s: descriptor len past pkt len",
+				    __func__);
+				chunk_size = pktsz - ofs - pkt_desc->len;
+			} else
+				chunk_size = pkt_desc->len;
+
 			/* Read packet from descriptor ring */
-			if (read_mem(pkt_desc->addr, pkt + ofs,
-			    pkt_desc->len)) {
+			if (read_mem(pkt_desc->addr, pkt + ofs, chunk_size)) {
 				log_warnx("vionet: packet read_mem error "
 				    "@ 0x%llx", pkt_desc->addr);
 				goto out;
@@ -1494,9 +1558,15 @@ vionet_notify_tx(struct vionet_dev *dev)
 			goto out;
 		}
 
+		/* Check we don't read beyond allocated pktsz */
+		if (pkt_desc->len > pktsz - ofs) {
+			log_warnx("%s: descriptor len past pkt len", __func__);
+			chunk_size = pktsz - ofs - pkt_desc->len;
+		} else
+			chunk_size = pkt_desc->len;
+
 		/* Read packet from descriptor ring */
-		if (read_mem(pkt_desc->addr, pkt + ofs,
-		    pkt_desc->len)) {
+		if (read_mem(pkt_desc->addr, pkt + ofs, chunk_size)) {
 			log_warnx("vionet: packet read_mem error @ "
 			    "0x%llx", pkt_desc->addr);
 			goto out;
Index: usr.sbin/vmd/virtio.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.h,v
diff -u -p -r1.35 virtio.h
--- usr.sbin/vmd/virtio.h	11 Dec 2019 06:45:16 -0000	1.35
+++ usr.sbin/vmd/virtio.h	13 May 2021 18:56:16 -0000
@@ -297,7 +297,7 @@ int vionet_notifyq(struct vionet_dev *);
 void vionet_notify_rx(struct vionet_dev *);
 int vionet_notify_tx(struct vionet_dev *);
 void vionet_process_rx(uint32_t);
-int vionet_enq_rx(struct vionet_dev *, char *, ssize_t, int *);
+int vionet_enq_rx(struct vionet_dev *, char *, size_t, int *);
 
 int vmmci_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
 int vmmci_dump(int);
