untrusted comment: verify with openbsd-79-base.pub
RWTSdNN9A3yvWJCwIekQJsPXSmniZaMO1i+MAv3h5w2JPmJBkNj+JE3U+7EO/CbYgVsYfEzir99CnOFVW1q2KSNpSzQZ0KdG4QE=

OpenBSD 7.9 errata 003, June 2, 2026:

In vmd(8), fix a variety of crashing bugs and misbehavior of -b flag.

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

And then rebuild and install vmd:
    cd /usr/src/usr.sbin/vmd
    make obj
    make
    make install

Index: usr.sbin/vmd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/config.c,v
diff -u -p -r1.82 config.c
--- usr.sbin/vmd/config.c	27 Apr 2026 13:06:14 -0000	1.82
+++ usr.sbin/vmd/config.c	29 May 2026 17:11:54 -0000
@@ -297,11 +297,9 @@ config_setvm(struct privsep *ps, struct 
 			goto fail;
 		}
 
-		if ((vm->vm_kernel = dup(kernfd)) == -1) {
-			ret = errno;
-			goto fail;
-		}
-		vmc->vmc_kernel = vm->vm_kernel;
+		vm->vm_kernel = kernfd;
+		vmc->vmc_kernel = kernfd;
+		kernfd = -1;
 	}
 
 	/* Open CDROM image for child */
@@ -484,6 +482,10 @@ config_setvm(struct privsep *ps, struct 
 
 	/* Send VM information */
 	/* XXX check proc_compose_imsg return values */
+	if ((kernfd = dup(vm->vm_kernel)) == -1) {
+		ret = errno;
+		goto fail;
+	}
 	proc_compose_imsg(ps, PROC_VMM, IMSG_VMDOP_START_VM_REQUEST,
 	    vm->vm_vmid, kernfd, vmc, sizeof(*vmc));
 
@@ -530,8 +532,6 @@ config_setvm(struct privsep *ps, struct 
  fail:
 	log_warnx("failed to start vm %s", vmc->vmc_name);
 
-	if (vm->vm_kernel != -1)
-		close(vm->vm_kernel);
 	if (kernfd != -1)
 		close(kernfd);
 	if (cdromfd != -1)
@@ -546,6 +546,7 @@ config_setvm(struct privsep *ps, struct 
 		free(tapfds);
 	}
 
+	/* Both vm_stop() and vm_remove() will close vm->vm_kernel. */
 	if (vm->vm_from_config) {
 		vm_stop(vm, 0, __func__);
 	} else {
Index: usr.sbin/vmd/vioblk.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vioblk.c,v
diff -u -p -r1.28 vioblk.c
--- usr.sbin/vmd/vioblk.c	14 Apr 2026 21:41:19 -0000	1.28
+++ usr.sbin/vmd/vioblk.c	29 May 2026 17:11:55 -0000
@@ -267,7 +267,7 @@ vioblk_notifyq(struct virtio_dev *dev, u
 	struct vioblk_dev *vioblk = &dev->vioblk;
 
 	/* Invalid queue? */
-	if (vq_idx > dev->num_queues)
+	if (vq_idx >= dev->num_queues)
 		return (0);
 
 	vq_info = &dev->vq[vq_idx];
@@ -284,6 +284,11 @@ vioblk_notifyq(struct virtio_dev *dev, u
 	while (idx != avail->idx) {
 		/* Retrieve Command descriptor. */
 		cmd_desc_idx = avail->ring[idx & vq_info->mask];
+		if (cmd_desc_idx >= vq_info->qs) {
+			log_warnx("%s: invalid head descriptor index",
+			    __func__);
+			goto reset;
+		}
 		desc = &table[cmd_desc_idx];
 		cmd_len = desc->len;
 
Index: usr.sbin/vmd/vioscsi.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vioscsi.c,v
diff -u -p -r1.29 vioscsi.c
--- usr.sbin/vmd/vioscsi.c	22 Feb 2026 22:54:54 -0000	1.29
+++ usr.sbin/vmd/vioscsi.c	29 May 2026 17:11:55 -0000
@@ -1501,6 +1501,11 @@ vioscsi_handle_read_10(struct virtio_dev
 		    __func__, acct->resp_desc->addr, acct->resp_desc->len,
 		    acct->resp_idx, acct->req_idx, acct->idx);
 
+		if (acct->resp_desc->len == 0) {
+			log_warnx("%s: zero-length read_buf descriptor", __func__);
+			goto free_read_10;
+		}
+
 		/* 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",
@@ -1516,7 +1521,7 @@ vioscsi_handle_read_10(struct virtio_dev
 			    acct->resp_desc->addr);
 			goto free_read_10;
 		}
-		chunk_offset += acct->resp_desc->len;
+		chunk_offset += chunk_len;
 	} while (chunk_offset < info->len);
 
 	ret = 1;
@@ -2189,7 +2194,17 @@ vioscsi_notifyq(struct virtio_dev *dev, 
 	struct virtio_vq_acct acct;
 	struct virtio_vq_info *vq_info;
 
+	if (vq_idx >= dev->num_queues) {
+		log_warnx("%s: invalid virtqueue index %u", __func__, vq_idx);
+		return (0);
+	}
+
 	vq_info = &dev->vq[vq_idx];
+	if (!vq_info->vq_enabled) {
+		log_warnx("%s: virtqueue not enabled", __func__);
+		return (0);
+	}
+
 	vr = vq_info->q_hva;
 	if (vr == NULL)
 		fatalx("%s: null vring", __func__);
Index: usr.sbin/vmd/virtio.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/virtio.c,v
diff -u -p -r1.137 virtio.c
--- usr.sbin/vmd/virtio.c	14 Apr 2026 21:41:19 -0000	1.137
+++ usr.sbin/vmd/virtio.c	29 May 2026 17:11:55 -0000
@@ -196,6 +196,7 @@ virtio_update_qa(struct virtio_dev *dev)
 {
 	struct virtio_vq_info *vq_info = NULL;
 	void *hva = NULL;
+	uint64_t availoff, usedoff, availsz, usedsz;
 
 	if (dev->driver_feature & VIRTIO_F_VERSION_1) {
 		if (dev->pci_cfg.queue_select >= dev->num_queues) {
@@ -212,11 +213,41 @@ virtio_update_qa(struct virtio_dev *dev)
 		vq_info->qs = dev->pci_cfg.queue_size;
 		vq_info->mask = vq_info->qs - 1;
 
+		/*
+		 * Require the available (driver) and used (device) area to be
+		 * similar to Virtio 0.9 but support Virtio 1.x alignment.
+		 */
+		if (dev->pci_cfg.queue_avail < dev->pci_cfg.queue_desc ||
+		    dev->pci_cfg.queue_used < dev->pci_cfg.queue_desc) {
+			vq_info->vq_enabled = 0;
+			return;
+		}
+
+		availoff = dev->pci_cfg.queue_avail - dev->pci_cfg.queue_desc;
+		usedoff = dev->pci_cfg.queue_used - dev->pci_cfg.queue_desc;
+		if (availoff > UINT32_MAX || usedoff > UINT32_MAX ||
+		    (usedoff & 3) != 0) {
+			vq_info->vq_enabled = 0;
+			return;
+		}
+
+		availsz = sizeof(uint16_t) * (2 + vq_info->qs);
+		usedsz = (sizeof(uint16_t) * 2) +
+		    (sizeof(struct vring_used_elem) * vq_info->qs);
+		hva = hvaddr_mem(dev->pci_cfg.queue_desc + availoff, availsz);
+		if (hva == NULL) {
+			vq_info->vq_enabled = 0;
+			return;
+		}
+		hva = hvaddr_mem(dev->pci_cfg.queue_desc + usedoff, usedsz);
+		if (hva == NULL) {
+			vq_info->vq_enabled = 0;
+			return;
+		}
+
 		if (vq_info->qs > 0 && vq_info->qs % 2 == 0) {
-			vq_info->vq_availoffset = dev->pci_cfg.queue_avail -
-			    dev->pci_cfg.queue_desc;
-			vq_info->vq_usedoffset = dev->pci_cfg.queue_used -
-			    dev->pci_cfg.queue_desc;
+			vq_info->vq_availoffset = availoff;
+			vq_info->vq_usedoffset = usedoff;
 			vq_info->vq_enabled = (dev->pci_cfg.queue_enable == 1);
 		} else {
 			vq_info->vq_availoffset = 0;
@@ -294,8 +325,10 @@ viornd_notifyq(struct virtio_dev *dev, u
 	dxx = avail->ring[aidx] & vq_info->mask;
 
 	sz = desc[dxx].len;
-	if (sz > MAXPHYS)
-		fatalx("viornd descriptor size too large (%zu)", sz);
+	if (sz > MAXPHYS) {
+		log_warnx("viornd descriptor size too large (%zu)", sz);
+		return (0);
+	}
 
 	rnd_data = malloc(sz);
 	if (rnd_data == NULL)
