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);