Skip to content

Commit da05d66

Browse files
virtio: setup virtqueues based on host capability
Previously we ignored the host's maximum virtqueues size and just blasted our own value into the device. QEMU handled this gracefully because it actually didn't check that what we wrote is under the limit, but as of 11.1.0 this is no longer the case and our drivers broke. See: https://freenode.net/article/qemu-fixes-virtio-mmio-queue-size-oob-cve-2026-50626 To fix this we must read what the host's maximum value is and clamp the virtqueue size accordingly. We also need to initialise the index allocator in the virtIO drivers with the corresponding range too, otherwise we will get an out of bound index from the allocator Signed-off-by: Bill Nguyen <bill.nguyen@unsw.edu.au>
1 parent 30be6d0 commit da05d66

5 files changed

Lines changed: 71 additions & 43 deletions

File tree

drivers/blk/virtio/block.c

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131

3232
virtio_device_handle_t dev;
3333

34-
#define QUEUE_SIZE 1024
35-
#define VIRTQ_NUM_REQUESTS QUEUE_SIZE
34+
#define QUEUE_SIZE_MAX 1024
3635

3736
uintptr_t requests_paddr;
3837
uintptr_t requests_vaddr;
@@ -47,14 +46,14 @@ static struct virtio_blk_req *virtio_headers;
4746
* A mapping from virtIO header index in the descriptor virtq ring, to the sDDF ID given
4847
* in the request. We need this mapping due to out of order operations.
4948
*/
50-
uint32_t virtio_header_to_id[QUEUE_SIZE];
49+
uint32_t virtio_header_to_id[QUEUE_SIZE_MAX];
5150

5251
/*
5352
* Due to the out-of-order nature of virtIO, we need a way of allocating indexes in a
5453
* non-linear way.
5554
*/
5655
ialloc_t ialloc_desc;
57-
uint32_t descriptors[QUEUE_SIZE];
56+
uint32_t descriptors[QUEUE_SIZE_MAX];
5857

5958
uint16_t last_seen_used = 0;
6059

@@ -271,7 +270,6 @@ void handle_irq(void)
271270
void virtio_blk_init(void)
272271
{
273272
assert(virtio_transport_probe(&device_resources, &dev, VIRTIO_DEVICE_ID_BLK));
274-
ialloc_init(&ialloc_desc, descriptors, QUEUE_SIZE);
275273

276274
/* First reset the device */
277275
virtio_transport_set_status(&dev, 0);
@@ -302,9 +300,6 @@ void virtio_blk_init(void)
302300
storage_info->block_size = 1;
303301
storage_info->sector_size = VIRTIO_BLK_SECTOR_SIZE;
304302

305-
/* Finished populating configuration */
306-
blk_storage_set_ready(storage_info, true);
307-
308303
#ifdef DEBUG_DRIVER
309304
uint32_t features_low = virtio_transport_get_driver_features(&dev, 0);
310305
uint32_t features_high = virtio_transport_get_driver_features(&dev, 1);
@@ -321,11 +316,15 @@ void virtio_blk_init(void)
321316
return;
322317
}
323318

319+
uint16_t host_q_capacity = virtio_transport_queue_get_capacity(&dev, 0);
320+
uint16_t driver_q_capacity = MIN(host_q_capacity, QUEUE_SIZE_MAX);
321+
ialloc_init(&ialloc_desc, descriptors, driver_q_capacity);
322+
324323
/* Add virtqueues */
325324
size_t desc_off = 0;
326-
size_t avail_off = ALIGN(desc_off + (16 * VIRTQ_NUM_REQUESTS), 2);
327-
size_t used_off = ALIGN(avail_off + (6 + 2 * VIRTQ_NUM_REQUESTS), 4);
328-
size_t size = used_off + (6 + 8 * VIRTQ_NUM_REQUESTS);
325+
size_t avail_off = ALIGN(desc_off + (16 * driver_q_capacity), 2);
326+
size_t used_off = ALIGN(avail_off + (6 + 2 * driver_q_capacity), 4);
327+
size_t size = used_off + (6 + 8 * driver_q_capacity);
329328

330329
// Make sure that the metadata region is able to fit all the virtIO specific
331330
// extra data.
@@ -337,12 +336,12 @@ void virtio_blk_init(void)
337336
assert(size <= device_resources.regions[2].region.size);
338337
#endif
339338

340-
virtq.num = VIRTQ_NUM_REQUESTS;
339+
virtq.num = driver_q_capacity;
341340
virtq.desc = (struct virtq_desc *)(requests_vaddr + desc_off);
342341
virtq.avail = (struct virtq_avail *)(requests_vaddr + avail_off);
343342
virtq.used = (struct virtq_used *)(requests_vaddr + used_off);
344343

345-
virtio_transport_queue_setup(&dev, 0, VIRTQ_NUM_REQUESTS, requests_paddr + desc_off, requests_paddr + avail_off,
344+
virtio_transport_queue_setup(&dev, 0, driver_q_capacity, requests_paddr + desc_off, requests_paddr + avail_off,
346345
requests_paddr + used_off);
347346

348347
/* Finish initialisation */
@@ -390,6 +389,10 @@ void init(void)
390389
virtio_blk_init();
391390

392391
blk_queue_init(&blk_queue, config.virt.req_queue.vaddr, config.virt.resp_queue.vaddr, config.virt.num_buffers);
392+
393+
/* Driver ready */
394+
blk_storage_info_t *storage_info = config.virt.storage_info.vaddr;
395+
blk_storage_set_ready(storage_info, true);
393396
}
394397

395398
void notified(sddf_channel ch)

drivers/network/virtio/common/ethernet.c

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,8 @@ __attribute__((__section__(".net_driver_config"))) net_driver_config_t config;
4343
uintptr_t hw_ring_buffer_vaddr;
4444
uintptr_t hw_ring_buffer_paddr;
4545

46-
#define RX_COUNT 512
47-
#define TX_COUNT 512
48-
#define MAX_COUNT MAX(RX_COUNT, TX_COUNT)
46+
#define RX_COUNT_MAX 512
47+
#define TX_COUNT_MAX 512
4948

5049
#define HW_RING_SIZE (0x10000)
5150

@@ -69,9 +68,9 @@ virtio_net_hdr_t *virtio_net_tx_headers;
6968
virtio_device_handle_t dev;
7069

7170
ialloc_t rx_ialloc_desc;
72-
uint32_t rx_descriptors[RX_COUNT];
71+
uint32_t rx_descriptors[RX_COUNT_MAX];
7372
ialloc_t tx_ialloc_desc;
74-
uint32_t tx_descriptors[TX_COUNT];
73+
uint32_t tx_descriptors[TX_COUNT_MAX];
7574

7675
static inline bool virtio_avail_full(struct virtq *virtq, ialloc_t *ialloc)
7776
{
@@ -330,16 +329,21 @@ static void eth_setup(void)
330329
#endif
331330

332331
// Setup the virtqueues
332+
uint16_t rx_q_host_capacity = virtio_transport_queue_get_capacity(&dev, VIRTIO_NET_RX_QUEUE);
333+
uint16_t tx_q_host_capacity = virtio_transport_queue_get_capacity(&dev, VIRTIO_NET_TX_QUEUE);
334+
335+
uint16_t rx_q_driver_capacity = MIN(rx_q_host_capacity, RX_COUNT_MAX);
336+
uint16_t tx_q_driver_capacity = MIN(tx_q_host_capacity, TX_COUNT_MAX);
333337

334338
size_t rx_desc_off = 0;
335-
size_t rx_avail_off = ALIGN(rx_desc_off + (16 * RX_COUNT), 2);
336-
size_t rx_used_off = ALIGN(rx_avail_off + (6 + 2 * RX_COUNT), 4);
337-
size_t tx_desc_off = ALIGN(rx_used_off + (6 + 8 * RX_COUNT), 16);
338-
size_t tx_avail_off = ALIGN(tx_desc_off + (16 * TX_COUNT), 2);
339-
size_t tx_used_off = ALIGN(tx_avail_off + (6 + 2 * TX_COUNT), 4);
340-
size_t virtq_size = tx_used_off + (6 + 8 * TX_COUNT);
341-
342-
rx_virtq.num = RX_COUNT;
339+
size_t rx_avail_off = ALIGN(rx_desc_off + (16 * rx_q_driver_capacity), 2);
340+
size_t rx_used_off = ALIGN(rx_avail_off + (6 + 2 * rx_q_driver_capacity), 4);
341+
size_t tx_desc_off = ALIGN(rx_used_off + (6 + 8 * rx_q_driver_capacity), 16);
342+
size_t tx_avail_off = ALIGN(tx_desc_off + (16 * tx_q_driver_capacity), 2);
343+
size_t tx_used_off = ALIGN(tx_avail_off + (6 + 2 * tx_q_driver_capacity), 4);
344+
size_t virtq_size = tx_used_off + (6 + 8 * tx_q_driver_capacity);
345+
346+
rx_virtq.num = rx_q_driver_capacity;
343347
rx_virtq.desc = (struct virtq_desc *)(hw_ring_buffer_vaddr + rx_desc_off);
344348
rx_virtq.avail = (struct virtq_avail *)(hw_ring_buffer_vaddr + rx_avail_off);
345349
rx_virtq.used = (struct virtq_used *)(hw_ring_buffer_vaddr + rx_used_off);
@@ -348,7 +352,7 @@ static void eth_setup(void)
348352
assert((uintptr_t)rx_virtq.avail % 2 == 0);
349353
assert((uintptr_t)rx_virtq.used % 4 == 0);
350354

351-
tx_virtq.num = TX_COUNT;
355+
tx_virtq.num = tx_q_driver_capacity;
352356
tx_virtq.desc = (struct virtq_desc *)(hw_ring_buffer_vaddr + tx_desc_off);
353357
tx_virtq.avail = (struct virtq_avail *)(hw_ring_buffer_vaddr + tx_avail_off);
354358
tx_virtq.used = (struct virtq_used *)(hw_ring_buffer_vaddr + tx_used_off);
@@ -361,22 +365,27 @@ static void eth_setup(void)
361365
virtio_net_tx_headers_vaddr = hw_ring_buffer_vaddr + virtq_size;
362366
virtio_net_tx_headers_paddr = hw_ring_buffer_paddr + virtq_size;
363367
virtio_net_tx_headers = (virtio_net_hdr_t *)virtio_net_tx_headers_vaddr;
364-
size_t tx_headers_size = ((TX_COUNT / 2) * sizeof(virtio_net_hdr_t));
368+
size_t tx_headers_size = ((tx_q_driver_capacity / 2) * sizeof(virtio_net_hdr_t));
365369
virtio_net_rx_headers_paddr = virtio_net_tx_headers_paddr + tx_headers_size;
366-
size_t rx_headers_size = ((RX_COUNT / 2) * sizeof(virtio_net_hdr_t));
370+
size_t rx_headers_size = ((rx_q_driver_capacity / 2) * sizeof(virtio_net_hdr_t));
367371

368372
assert(virtq_size + tx_headers_size + rx_headers_size <= HW_RING_SIZE);
369373

374+
ialloc_init(&rx_ialloc_desc, rx_descriptors, rx_q_driver_capacity);
375+
ialloc_init(&tx_ialloc_desc, tx_descriptors, tx_q_driver_capacity);
376+
370377
rx_provide();
371378
tx_provide();
372379

373380
// Setup RX queue first
374-
assert(virtio_transport_queue_setup(&dev, VIRTIO_NET_RX_QUEUE, RX_COUNT, hw_ring_buffer_paddr + rx_desc_off,
375-
hw_ring_buffer_paddr + rx_avail_off, hw_ring_buffer_paddr + rx_used_off));
381+
assert(virtio_transport_queue_setup(&dev, VIRTIO_NET_RX_QUEUE, rx_q_driver_capacity,
382+
hw_ring_buffer_paddr + rx_desc_off, hw_ring_buffer_paddr + rx_avail_off,
383+
hw_ring_buffer_paddr + rx_used_off));
376384

377385
// Setup TX queue
378-
assert(virtio_transport_queue_setup(&dev, VIRTIO_NET_TX_QUEUE, TX_COUNT, hw_ring_buffer_paddr + tx_desc_off,
379-
hw_ring_buffer_paddr + tx_avail_off, hw_ring_buffer_paddr + tx_used_off));
386+
assert(virtio_transport_queue_setup(&dev, VIRTIO_NET_TX_QUEUE, tx_q_driver_capacity,
387+
hw_ring_buffer_paddr + tx_desc_off, hw_ring_buffer_paddr + tx_avail_off,
388+
hw_ring_buffer_paddr + tx_used_off));
380389

381390
// Set the MAC address
382391
config->mac[0] = 0x52;
@@ -411,9 +420,6 @@ void init(void)
411420
hw_ring_buffer_paddr = device_resources.regions[1].io_addr;
412421
#endif
413422

414-
ialloc_init(&rx_ialloc_desc, rx_descriptors, RX_COUNT);
415-
ialloc_init(&tx_ialloc_desc, tx_descriptors, TX_COUNT);
416-
417423
net_queue_init(&rx_queue, config.virt_rx.free_queue.vaddr, config.virt_rx.active_queue.vaddr,
418424
config.virt_rx.num_buffers);
419425
net_queue_init(&tx_queue, config.virt_tx.free_queue.vaddr, config.virt_tx.active_queue.vaddr,

include/sddf/virtio/transport/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ uint32_t virtio_transport_get_device_features(virtio_device_handle_t *device_han
5757
uint32_t virtio_transport_get_driver_features(virtio_device_handle_t *device_handle, uint32_t select);
5858
void virtio_transport_set_driver_features(virtio_device_handle_t *device_handle, uint32_t select,
5959
uint32_t driver_features);
60+
uint16_t virtio_transport_queue_get_capacity(virtio_device_handle_t *device_handle, uint32_t select);
6061
bool virtio_transport_queue_setup(virtio_device_handle_t *device_handle, uint32_t select, uint16_t size, uint64_t desc,
6162
uint64_t driver, uint64_t device);
6263
void virtio_transport_queue_notify(virtio_device_handle_t *device_handle, uint32_t select);

virtio/transport/mmio.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,25 @@ void virtio_transport_set_driver_features(virtio_device_handle_t *device_handle,
9595
regs->DriverFeatures = driver_features;
9696
}
9797

98-
bool virtio_transport_queue_setup(virtio_device_handle_t *device_handle, uint32_t select, uint16_t size, uint64_t desc,
99-
uint64_t driver, uint64_t device)
98+
uint16_t virtio_transport_queue_get_capacity(virtio_device_handle_t *device_handle, uint32_t select)
10099
{
101100
volatile virtio_mmio_regs_t *regs = get_regs(device_handle->device_resources);
102-
103101
regs->QueueSel = select;
102+
return regs->QueueNumMax;
103+
}
104104

105-
if (regs->QueueNumMax < size) {
106-
LOG_VIRTIO_TRANSPORT("virtio queue is smaller than virtqueue!\n");
105+
bool virtio_transport_queue_setup(virtio_device_handle_t *device_handle, uint32_t select, uint16_t size, uint64_t desc,
106+
uint64_t driver, uint64_t device)
107+
{
108+
uint16_t hw_max_capacity = virtio_transport_queue_get_capacity(device_handle, select);
109+
if (size > hw_max_capacity) {
110+
LOG_VIRTIO_ERR("Requested queue size %u is larger than host's max %u!\n", size, hw_max_capacity);
107111
return false;
108112
}
109113

110-
regs->QueueNum = (uint32_t)size;
114+
volatile virtio_mmio_regs_t *regs = get_regs(device_handle->device_resources);
115+
regs->QueueSel = select;
116+
regs->QueueNum = size;
111117
regs->QueueDescLow = desc & 0xffffffff;
112118
regs->QueueDescHigh = desc >> 32;
113119
regs->QueueDriverLow = driver & 0xffffffff;

virtio/transport/pci.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,23 @@ void virtio_transport_set_driver_features(virtio_device_handle_t *device_handle,
246246
cfg->driver_feature = driver_features;
247247
}
248248

249+
uint16_t virtio_transport_queue_get_capacity(virtio_device_handle_t *device_handle, uint32_t select)
250+
{
251+
virtio_pci_common_cfg_t *cfg = get_cfg(device_handle->device_resources);
252+
cfg->queue_select = select;
253+
return cfg->queue_size;
254+
}
255+
249256
bool virtio_transport_queue_setup(virtio_device_handle_t *device_handle, uint32_t select, uint16_t size, uint64_t desc,
250257
uint64_t driver, uint64_t device)
251258
{
252-
virtio_pci_common_cfg_t *cfg = get_cfg(device_handle->device_resources);
259+
uint16_t hw_max_capacity = virtio_transport_queue_get_capacity(device_handle, select);
260+
if (size > hw_max_capacity) {
261+
LOG_VIRTIO_ERR("Requested queue size %u is larger than host's max %u!\n", size, hw_max_capacity);
262+
return false;
263+
}
253264

265+
virtio_pci_common_cfg_t *cfg = get_cfg(device_handle->device_resources);
254266
cfg->queue_select = select;
255267
cfg->queue_size = size;
256268
cfg->queue_desc = desc;

0 commit comments

Comments
 (0)