-/* $OpenBSD: virtio.c,v 1.85 2021/04/21 18:27:36 dv Exp $ */
+/* $OpenBSD: virtio.c,v 1.86 2021/04/22 18:40:21 dv Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
+#include <netinet/ip.h>
#include <errno.h>
#include <event.h>
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 "
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 "
}
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;
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;
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);
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;
goto out;
}
+ cnt = 0;
secbias = 0;
do {
struct ioinfo *info;
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;
{
uint64_t q_gpa;
uint32_t vr_sz;
- uint16_t idx, pkt_desc_idx, hdr_desc_idx, dxx;
+ uint16_t idx, pkt_desc_idx, hdr_desc_idx, dxx, cnt;
size_t pktsz;
ssize_t dhcpsz;
int ret, num_enq, ofs, spc;
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;
/* 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");