virt: bao: add Bao hypervisor IPC and I/O dispatcher drivers
Subsystems (2)
v4 cover virt: bao: add Bao hypervisor IPC and I/O dispatcher drivers
This series adds guest-side drivers for the Bao static-partitioning
hypervisor: an IPC shared-memory driver, an I/O dispatcher that lets a backend guest service VirtIO I/O for frontend guests, their UAPI and a MAINTAINERS entry.
Bao is a lightweight static-partitioning hypervisor for embedded and safety-critical systems (https://github.com/bao-project).
- The IPC shared-memory driver lets Bao guests exchange data through a shared-memory region split into a read and a write channel, exposed as a misc character device (read(), write(), mmap()).
- The I/O dispatcher bridges Bao's Remote I/O mechanism to a userspace
VMM: the VMM creates each device model from /dev/bao, receives the frontend's MMIO accesses and completes them, with ioeventfd/irqfd support for the fast paths.
Changes since v3
----------------
The two design comments on v3 changed the shape of the series, which went from six patches to three.
- No device tree (Krzysztof Kozlowski): both bindings and the "bao" vendor prefix are dropped. The IPC channels are a software contract between the hypervisor and the guest and are now declared on the kernel command line (bao_ipcshmem.channels=...). The set of device models a backend serves is a contract between the hypervisor and the VMM, so, following drivers/virt/acrn, the I/O dispatcher exposes a single /dev/bao control device and the VMM creates each device model with BAO_IOCTL_CREATE_DM (shared-memory region + notification line), which returns a per-DM file descriptor; the DM lives as long as the descriptor. The notification line is resolved against the device tree's root interrupt parent, like any device's interrupt.
- No architecture code (Will Deacon): the hypercall helpers moved to drivers/virt/bao/bao_hypercall.h and use arm_smccc_hvc() for the IPC hypercall and, because the Remote I/O hypercall returns the request in x1-x6, the SMCCC v1.2 arm_smccc_1_2_hvc(). RISC-V uses sbi_ecall() for IPC and a local ecall for Remote I/O (see open items). 32-bit Arm has no SMCCC v1.2 helper, so the I/O dispatcher is limited to arm64 and RISC-V for now; the IPC driver still supports 32-bit Arm through SMCCC v1.1 (HAVE_ARM_SMCCC).
- The standalone "consolidate the IPC hypercall ID" patch is folded into the driver patches (Andrew Jones).
- All findings of the Sashiko review of v3 are addressed, among them:
the DM id is taken from the file descriptor instead of userspace; the irqfd/ioeventfd teardown races and the ioeventfd deassign fall-through are fixed; the interrupt handler is per DM and the request_irq() name is persistent; queued requests are capped and requests that cannot be delivered are completed back to the hypervisor; the UAPI structures have no implicit padding; signals return -ERESTARTSYS; the IPC driver orders the shared-memory writes before the notify hypercall, refuses writable mappings of the read region, validates page-aligned regions and has an llseek. Details are in the per-patch changelogs.
- The ioctl type is now 0xA7: 7.3-rc1 registered 0xA6 for memory allocation profiling.
- Found while testing: the two modules were both named bao.ko (now bao_ipcshmem and bao_io_dispatcher), the ioeventfd kernel thread could exit before kthread_stop(), per-DM dispatcher state was kept in static arrays, a list walk used the wrong structure type, the UAPI header did not include linux/ioctl.h, and the dispatcher workqueue now passes WQ_PERCPU as required since 7.x.
Testing
-------
Built on v7.3-rc1 with GCC (W=1, sparse) for arm64, arm and riscv, as modules and built-in, and with clang for arm64 and riscv. Run-time tested under Bao v2.0.0 on QEMU aarch64 virt and QEMU riscv64 virt with the bao-demos demos: the IPC driver exchanging messages in both directions between a Linux 7.3-rc1 guest and a FreeRTOS guest (linux+freertos demo), and the I/O dispatcher with a Linux backend serving console, network and block device models to a FreeRTOS guest and two Linux guests (virtio demo). 32-bit Arm is compile-tested only.
Open items
----------
- RISC-V: the Bao SBI extension still uses the experimental extension space (0x08000ba0), and the Remote I/O hypercall returns the request in a2-a7, which does not follow the SBI calling convention (Andrew Jones, v2). Changing that means a hypervisor ABI change (status in a0/a1, request through shared memory); the RISC-V support is marked experimental until then, or it can be split out of this series if preferred.
- Bao does not implement the SMCCC vendor-hypervisor UID call, so the drivers cannot detect the hypervisor yet; adding it is planned on the hypervisor side.
v3: https://lore.kernel.org/all/[email protected]/
João Peixoto (3):
virt: bao: add IPC shared-memory driver
virt: bao: add I/O dispatcher driver
MAINTAINERS: add Bao hypervisor entry
.../userspace-api/ioctl/ioctl-number.rst | 2 + MAINTAINERS | 8 + drivers/virt/Kconfig | 2 + drivers/virt/Makefile | 1 + drivers/virt/bao/Kconfig | 5 + drivers/virt/bao/Makefile | 4 + drivers/virt/bao/bao_hypercall.h | 190 ++++++++ drivers/virt/bao/io-dispatcher/Kconfig | 20 + drivers/virt/bao/io-dispatcher/Makefile | 4 + drivers/virt/bao/io-dispatcher/bao_drv.h | 389 ++++++++++++++++ drivers/virt/bao/io-dispatcher/dm.c | 334 ++++++++++++++ drivers/virt/bao/io-dispatcher/driver.c | 70 +++ drivers/virt/bao/io-dispatcher/intc.c | 150 +++++++ drivers/virt/bao/io-dispatcher/io_client.c | 423 ++++++++++++++++++ .../virt/bao/io-dispatcher/io_dispatcher.c | 159 +++++++ drivers/virt/bao/io-dispatcher/ioeventfd.c | 326 ++++++++++++++ drivers/virt/bao/io-dispatcher/irqfd.c | 315 +++++++++++++ drivers/virt/bao/ipcshmem/Kconfig | 16 + drivers/virt/bao/ipcshmem/Makefile | 3 + drivers/virt/bao/ipcshmem/ipcshmem.c | 358 +++++++++++++++ include/uapi/linux/bao.h | 116 +++++ 21 files changed, 2895 insertions(+) create mode 100644 drivers/virt/bao/Kconfig create mode 100644 drivers/virt/bao/Makefile create mode 100644 drivers/virt/bao/bao_hypercall.h create mode 100644 drivers/virt/bao/io-dispatcher/Kconfig create mode 100644 drivers/virt/bao/io-dispatcher/Makefile create mode 100644 drivers/virt/bao/io-dispatcher/bao_drv.h create mode 100644 drivers/virt/bao/io-dispatcher/dm.c create mode 100644 drivers/virt/bao/io-dispatcher/driver.c create mode 100644 drivers/virt/bao/io-dispatcher/intc.c create mode 100644 drivers/virt/bao/io-dispatcher/io_client.c create mode 100644 drivers/virt/bao/io-dispatcher/io_dispatcher.c create mode 100644 drivers/virt/bao/io-dispatcher/ioeventfd.c create mode 100644 drivers/virt/bao/io-dispatcher/irqfd.c create mode 100644 drivers/virt/bao/ipcshmem/Kconfig create mode 100644 drivers/virt/bao/ipcshmem/Makefile create mode 100644 drivers/virt/bao/ipcshmem/ipcshmem.c create mode 100644 include/uapi/linux/bao.h
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
Signature · 1 line
2.43.0
PATCH v4 1/3 virt: bao: add IPC shared-memory driver
Add a driver that lets guests running on the Bao static-partitioning hypervisor communicate through shared memory. Each guest is assigned a read and a write region within a shared-memory area.
The IPC channels are a pure software contract between the Bao hypervisor and its guests, so they are not described in the device tree. Each channel is instead declared on the kernel command line:
bao_ipcshmem.channels=<channel>[;<channel>...]
where each <channel> is
<id>,<read_base>,<read_size>,<write_base>,<write_size>
Userspace accesses the regions through a misc character device using read(), write() and mmap(). The read region is written by the peer and mapped read-only at stage 2, so a writable mapping of it is refused. A write() notifies the peer guest through a hypercall issued with the architecture's standard hypervisor call convention: an SMCCC fast call in the vendor-specific hypervisor service range (HVC) on arm/arm64 and an SBI extension call (ecall) on RISC-V. The helpers live in the driver directory, built on the generic SMCCC and SBI support, so no architecture code is needed.
---
v4:
- Drop the device-tree binding and the platform driver; the channels are declared on the kernel command line (bao_ipcshmem.channels=<id>,<read_base>, <read_size>,<write_base>,<write_size>[;...]) and the misc devices are created from module init (Krzysztof Kozlowski).
- Move the hypercall helper out of arch/ into drivers/virt/bao/bao_hypercall.h, built on arm_smccc_hvc() (arm/arm64) and sbi_ecall() (RISC-V); the hypercall ID is defined there, folding the v3 "consolidate the IPC hypercall ID" patch (Will Deacon, Andrew Jones).
- Kconfig: depend on HAVE_ARM_SMCCC || RISCV; the module is now bao_ipcshmem, so the documented parameter name is real.
- Add wmb() before the notify hypercall; refuse writable mappings of the read region and clear VM_MAYWRITE; require page-aligned regions and reject ranges that do not fit phys_addr_t/size_t; add .llseek; enlarge the label buffer; do the mmap offset arithmetic in u64 (Sashiko review).
- Devices live and die with the module and open files pin it, closing the unbind use-after-free of v3.
drivers/virt/Kconfig | 2 + drivers/virt/Makefile | 1 + drivers/virt/bao/Kconfig | 3 + drivers/virt/bao/Makefile | 3 + drivers/virt/bao/bao_hypercall.h | 90 +++++++ drivers/virt/bao/ipcshmem/Kconfig | 16 ++ drivers/virt/bao/ipcshmem/Makefile | 3 + drivers/virt/bao/ipcshmem/ipcshmem.c | 358 +++++++++++++++++++++++++++ 8 files changed, 476 insertions(+) create mode 100644 drivers/virt/bao/Kconfig create mode 100644 drivers/virt/bao/Makefile create mode 100644 drivers/virt/bao/bao_hypercall.h create mode 100644 drivers/virt/bao/ipcshmem/Kconfig create mode 100644 drivers/virt/bao/ipcshmem/Makefile create mode 100644 drivers/virt/bao/ipcshmem/ipcshmem.c
drivers/virt/Kconfig +2 -0
@@ -47,6 +47,8 @@ source "drivers/virt/nitro_enclaves/Kconfig" source "drivers/virt/acrn/Kconfig" +source "drivers/virt/bao/Kconfig" + endif source "drivers/virt/coco/Kconfig"
drivers/virt/Makefile +1 -0
@@ -10,3 +10,4 @@ obj-y += vboxguest/ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ obj-$(CONFIG_ACRN_HSM) += acrn/ obj-y += coco/ +obj-y += bao/
drivers/virt/bao/Kconfig new file +3 -0
@@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +source "drivers/virt/bao/ipcshmem/Kconfig"
drivers/virt/bao/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_BAO_SHMEM) += ipcshmem/
drivers/virt/bao/bao_hypercall.h new file +90 -0
90 lines not on this page · raw message
drivers/virt/bao/ipcshmem/Kconfig new file +16 -0
@@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +config BAO_SHMEM + tristate "Bao hypervisor shared memory support" + depends on HAVE_ARM_SMCCC || RISCV + help + This enables support for Bao shared memory communication. + It allows the kernel to interface with guests running under + the Bao hypervisor, providing a character device interface + for exchanging data through dedicated shared-memory regions. + Channels are declared on the kernel command line via + "bao_ipcshmem.channels=". + + To compile this driver as a module, choose M here: the module + will be called bao_ipcshmem. + + If unsure, say N.
drivers/virt/bao/ipcshmem/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_BAO_SHMEM) += bao_ipcshmem.o +bao_ipcshmem-y := ipcshmem.o
drivers/virt/bao/ipcshmem/ipcshmem.c new file +358 -0
358 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v4 1/3] virt: bao: add IPC shared-memory driver
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] virt: bao: UAF on module initialization failure --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] virt: bao: UAF on module initialization failure
--
commit 2f861bb98100794551d8aebc0473ce9f71733cac
Author: João Peixoto <[email protected]>
virt: bao: add IPC shared-memory driver
This commit introduces a new IPC shared-memory driver for the Bao hypervisor. It allows guests to communicate through read and write regions configured via the bao_ipcshmem.channels module parameter, exposing each channel as a misc character device to userspace.
Quoted 5 lines
drivers/virt/bao/ipcshmem/ipcshmem.c new file +0
-0@@ -0,0 +1,358 @@
[ ... ]
Quoted 10 lines
drivers/virt/bao/ipcshmem/ipcshmem.c new file +10
-0+static int __init bao_ipcshmem_init (void ) +{ +char *buf, *p, *desc; +int ret =0 ; + +if (!channels || !*channels) +return 0 ; + + buf =kstrdup (channels,GFP_KERNEL ); +if (!buf)
drivers/virt/bao/ipcshmem/ipcshmem.c line 337
Quoted 10 lines
drivers/virt/bao/ipcshmem/ipcshmem.c new file +10
-0+return -ENOMEM ; + + p = buf; +while ((desc =strsep (&p,";" )) !=NULL ) { +if (!*desc) +continue ; + ret =bao_ipcshmem_parse_one (desc); +if (ret) { +pr_err ("bad 'channels' descriptor\n" ); +bao_ipcshmem_remove_all ();
[Severity: High]
Can this error path lead to a use-after-free if userspace has already opened a character device?
When multiple channels are specified in the module parameter, bao_ipcshmem_init() parses them in a loop. For each successfully parsed channel, bao_ipcshmem_add() immediately exposes the device to userspace via misc_register():
bao_ipcshmem_init()
bao_ipcshmem_parse_one()
bao_ipcshmem_add()
misc_register(&bao->miscdev);If a subsequent channel fails to parse, bao_ipcshmem_init() takes this error path and calls bao_ipcshmem_remove_all(). This unregisters and frees all previously registered channels.
If a user-space process (such as udev) opens the first exposed character device before the initialization failure occurs, the file descriptor remains active. However, the initialization failure causes the kernel to unload the module and free its memory. Subsequent system calls on the active file descriptor would attempt to access the unmapped file_operations structure and the freed struct bao_ipcshmem data, crashing the system.
Would it be safer to defer calling misc_register() until all channels have been successfully parsed and added to the list?
Quoted 7 lines
drivers/virt/bao/ipcshmem/ipcshmem.c new file +7
-0+break ; + } + } + +kfree (buf); +return ret; +}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
PATCH v4 2/3 virt: bao: add I/O dispatcher driver
In reply to João PeixotoAdd the Bao I/O dispatcher, used by backend VMs to service I/O on behalf of frontend guests. It bridges Bao's Remote I/O mechanism to userspace VirtIO backend device models.
The set of device models a backend serves is a software contract between the hypervisor and the userspace VMM, not hardware, so it is not described in the device tree. Following the model used by other hypervisor drivers (e.g. drivers/virt/acrn), the driver exposes a single /dev/bao control device; the VMM creates each device model from its own configuration with the BAO_IOCTL_CREATE_DM ioctl, which returns a per-DM file descriptor. Each device model has a contiguous shared-memory region for exchanging I/O buffers with its frontend and an interrupt the hypervisor uses to signal pending requests; both are provided by the VMM at creation time. The interrupt is given as a line of the system's root interrupt controller (the one the device tree's root node points at through interrupt-parent) and mapped by the driver. Userspace then drives the device model through a set of ioctls on that descriptor.
The Remote I/O hypercall returns the pending request in x1-x6 on top of the status in x0, so on arm64 it is issued as an SMCCC v1.2 call through arm_smccc_1_2_hvc(); no architecture code is needed. 32-bit Arm has no SMCCC v1.2 helper, so the I/O dispatcher is limited to arm64 and RISC-V for now. On RISC-V the request comes back in a2-a7 through the (experimental) Bao SBI extension, which sbi_ecall() cannot express, hence the local ecall helper.
---
v4:
- Drop the binding and the platform device: a single /dev/bao control device; BAO_IOCTL_CREATE_DM takes {shmem_addr, shmem_size, id, irq} from the VMM and returns a per-DM anonymous-inode fd; the DM lives as long as the fd (Krzysztof Kozlowski; also removes the unbind use-after-free).
- Resolve the notification line against the device tree's root interrupt parent instead of binding to a node (1-cell PLIC, 2-cell APLIC and 3/4-cell GIC specifiers).
- Issue the Remote I/O hypercall with arm_smccc_1_2_hvc() on arm64 (the request is returned in x1-x6, an SMCCC v1.2 call); no arch/ code. RISC-V keeps a local ecall since sbi_ecall() only exposes a0/a1. 32-bit Arm is not supported by this driver for now (no SMCCC v1.2 helper) (Will Deacon).
- UAPI: ioctl type 0xA7 (0xA6 is taken by alloc_tag since 7.3-rc1); struct bao_dm_info reordered to remove implicit padding, ioeventfd and irqfd flag values exported, ioeventfd fd signed, linux/ioctl.h included.
- Sashiko review: take the DM id from the fd, not from userspace; hold the irqfd lock across vfs_poll(), single-shot cleanup work and flush before destroy_workqueue(); per-DM interrupt handler; persistent request_irq() name; return after ioeventfd deassign and match the full {fd, addr, len}; cap queued requests; propagate -ERESTARTSYS; free pending requests on destroy; drop the unused kernel mapping of the shared memory; complete requests that cannot be delivered so the frontend does not hang.
- Found while testing: the kernel thread only exits through kthread_stop(); per-DM workqueue/work instead of static arrays (no more 16-DM limit); WQ_PERCPU on the dispatcher workqueue; fix the range-list walk type in bao_io_client_destroy(); module renamed to bao_io_dispatcher; ERR_PTR() errors from bao_dm_create().
.../userspace-api/ioctl/ioctl-number.rst | 2 + drivers/virt/bao/Kconfig | 2 + drivers/virt/bao/Makefile | 1 + drivers/virt/bao/bao_hypercall.h | 100 +++++ drivers/virt/bao/io-dispatcher/Kconfig | 20 + drivers/virt/bao/io-dispatcher/Makefile | 4 + drivers/virt/bao/io-dispatcher/bao_drv.h | 389 ++++++++++++++++ drivers/virt/bao/io-dispatcher/dm.c | 334 ++++++++++++++ drivers/virt/bao/io-dispatcher/driver.c | 70 +++ drivers/virt/bao/io-dispatcher/intc.c | 150 +++++++ drivers/virt/bao/io-dispatcher/io_client.c | 423 ++++++++++++++++++ .../virt/bao/io-dispatcher/io_dispatcher.c | 159 +++++++ drivers/virt/bao/io-dispatcher/ioeventfd.c | 326 ++++++++++++++ drivers/virt/bao/io-dispatcher/irqfd.c | 315 +++++++++++++ include/uapi/linux/bao.h | 116 +++++ 15 files changed, 2411 insertions(+) create mode 100644 drivers/virt/bao/io-dispatcher/Kconfig create mode 100644 drivers/virt/bao/io-dispatcher/Makefile create mode 100644 drivers/virt/bao/io-dispatcher/bao_drv.h create mode 100644 drivers/virt/bao/io-dispatcher/dm.c create mode 100644 drivers/virt/bao/io-dispatcher/driver.c create mode 100644 drivers/virt/bao/io-dispatcher/intc.c create mode 100644 drivers/virt/bao/io-dispatcher/io_client.c create mode 100644 drivers/virt/bao/io-dispatcher/io_dispatcher.c create mode 100644 drivers/virt/bao/io-dispatcher/ioeventfd.c create mode 100644 drivers/virt/bao/io-dispatcher/irqfd.c create mode 100644 include/uapi/linux/bao.h
Documentation/userspace-api/ioctl/ioctl-number.rst +2 -0
@@ -348,6 +348,8 @@ Code Seq# Include File Comments <mailto:[email protected]> 0xA6 00-0F uapi/linux/alloc_tag.h Memory allocation profiling <mailto:[email protected]> +0xA7 all uapi/linux/bao.h Bao hypervisor + <mailto:[email protected]> 0xAA 00-3F linux/uapi/linux/userfaultfd.h 0xAB 00-1F linux/nbd.h 0xAC 00-1F linux/raw.h
drivers/virt/bao/Kconfig +2 -0
@@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 source "drivers/virt/bao/ipcshmem/Kconfig" + +source "drivers/virt/bao/io-dispatcher/Kconfig"
drivers/virt/bao/Makefile +1 -0
@@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_BAO_SHMEM) += ipcshmem/ +obj-$(CONFIG_BAO_IO_DISPATCHER) += io-dispatcher/
drivers/virt/bao/bao_hypercall.h +100 -0
118 lines not on this page · raw message
drivers/virt/bao/io-dispatcher/Kconfig new file +20 -0
@@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0 +config BAO_IO_DISPATCHER + tristate "Bao Hypervisor I/O Dispatcher" + depends on ARM64 || RISCV + select EVENTFD + help + The Bao I/O Dispatcher is a kernel module for backend Linux VMs + running under the Bao hypervisor. It establishes the connection + between the Remote I/O system (Bao's mechanism for forwarding + I/O requests from frontend VMs to the backend VMs) and the + VirtIO backend device. + + This provides a unified API to support various VirtIO backends, + allowing Bao guests to perform I/O through the hypervisor + transparently. + + To compile this driver as a module, choose M here: the module + will be called bao_io_dispatcher. + + If unsure, say N.
drivers/virt/bao/io-dispatcher/Makefile new file +4 -0
@@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_BAO_IO_DISPATCHER) += bao_io_dispatcher.o +bao_io_dispatcher-y := driver.o dm.o intc.o io_client.o io_dispatcher.o \ + ioeventfd.o irqfd.o
drivers/virt/bao/io-dispatcher/bao_drv.h new file +389 -0
389 lines not on this page · raw message
drivers/virt/bao/io-dispatcher/dm.c new file +334 -0
334 lines not on this page · raw message
drivers/virt/bao/io-dispatcher/driver.c new file +70 -0
70 lines not on this page · raw message
drivers/virt/bao/io-dispatcher/intc.c new file +150 -0
150 lines not on this page · raw message
drivers/virt/bao/io-dispatcher/io_client.c new file +423 -0
423 lines not on this page · raw message
drivers/virt/bao/io-dispatcher/io_dispatcher.c new file +159 -0
159 lines not on this page · raw message
drivers/virt/bao/io-dispatcher/ioeventfd.c new file +326 -0
326 lines not on this page · raw message
drivers/virt/bao/io-dispatcher/irqfd.c new file +315 -0
315 lines not on this page · raw message
include/uapi/linux/bao.h new file +116 -0
116 lines not on this page · raw message
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH v4 2/3] virt: bao: add I/O dispatcher driver
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider: - [Critical] virt: bao: arbitrary physical memory mapping bypasses lockdown/CAP_SYS_RAWIO - [High] virt…
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Critical] virt: bao: arbitrary physical memory mapping bypasses lockdown/CAP_SYS_RAWIO
- [High] virt: bao: arbitrary hardware interrupt hijacking via user-provided IRQ
- [High] virt: bao: io_dispatcher workqueue permanently aborts fetching on request errors
- [High] virt: bao: unbounded allocation of ioeventfd structures allows DoS
- [Medium] virt: bao: queued I/O requests are silently dropped on client destruction
- [Medium] virt: bao: IRQ mapping leak in bao_intc_init error path
--
commit 59521b5bbcab95a835912872dd83d2870c2d8118
Author: João Peixoto <[email protected]>
virt: bao: add I/O dispatcher driver
Add the Bao I/O dispatcher, used by backend VMs to service I/O on behalf of frontend guests. It bridges Bao's Remote I/O mechanism to userspace VirtIO backend device models through a control device.
Quoted 2 lines
drivers/virt/bao/io-dispatcher/dm.c new file +0
-0
[ ... ]
Quoted 19 lines
drivers/virt/bao/io-dispatcher/dm.c new file +19
-0+static int bao_dm_mmap (struct file *filp,struct vm_area_struct *vma) +{ +struct bao_dm *dm = filp->private_data; +unsigned long vsize; +unsigned long offset; +phys_addr_t phys; + +if (WARN_ON_ONCE (!dm)) +return -ENODEV ; + + vsize = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff <<PAGE_SHIFT ; + +if (!vsize || offset) +return -EINVAL ; + +if (vsize > dm->info.shmem_size) +return -EINVAL ; +
drivers/virt/bao/io-dispatcher/dm.c line 187
Quoted 10 lines
drivers/virt/bao/io-dispatcher/dm.c new file +10
-0+ phys = dm->info.shmem_addr; +if (!PAGE_ALIGNED (phys)) +return -EINVAL ; + +if (remap_pfn_range (vma, vma->vm_start, phys >>PAGE_SHIFT , vsize, + vma->vm_page_prot)) +return -EFAULT ; + +return 0 ; +}
[Severity: Critical]
Is there any validation on the physical address passed from userspace via BAO_IOCTL_CREATE_DM?
Since dm->info.shmem_addr originates from userspace configuration without any kernel-enforced bounds, an attacker could provide an arbitrary physical address. Typically, mapping arbitrary physical memory requires lockdown checks or CAP_SYS_RAWIO.
Could this bypass security boundaries by mapping kernel text, data, or sensitive hardware registers directly into userspace?
Quoted 2 lines
drivers/virt/bao/io-dispatcher/intc.c new file +0
-0
[ ... ]
Quoted 2 lines
drivers/virt/bao/io-dispatcher/intc.c new file +2
-0+static int bao_intc_map_irq (u32 line) +{
[ ... ]
Quoted 8 lines
drivers/virt/bao/io-dispatcher/intc.c new file +8
-0+switch (cells) { +case 1 : + oirq.args[0 ] = line; +break ; +case 2 : + oirq.args[0 ] = line; + oirq.args[1 ] =IRQ_TYPE_EDGE_RISING ; +break ;
[ ... ]
Quoted 9 lines
drivers/virt/bao/io-dispatcher/intc.c new file +9
-0+int bao_intc_init (struct bao_dm *dm) +{ +int virq; + +if (WARN_ON_ONCE (!dm)) +return -EINVAL ; + + virq =bao_intc_map_irq (dm->info.irq); +if (virq <0 )
drivers/virt/bao/io-dispatcher/intc.c line 141
Quoted 10 lines
drivers/virt/bao/io-dispatcher/intc.c new file +10
-0+return virq; + + dm->virq = virq; + +scnprintf (dm->intc_name,sizeof (dm->intc_name),"bao-iodintc%u" , + dm->info.id); + +return request_irq (dm->virq, bao_interrupt_handler,0 , dm->intc_name, + dm); +}
[Severity: High]
Does this code allow an unprivileged userspace process to hijack arbitrary hardware interrupts?
Since dm->info.irq is completely controlled by userspace, translating it into a Linux IRQ mapping and immediately requesting it could allow a malicious process to intercept critical host system interrupts like timers or IPIs.
[Severity: Medium]
Also, if request_irq() fails here, is it possible we leak the IRQ mapping?
bao_intc_map_irq() calls irq_create_of_mapping(). Returning the error code directly without calling irq_dispose_mapping() on dm->virq could result in permanently leaking the descriptor mapping if the ioctl fails repeatedly.
Quoted 2 lines
drivers/virt/bao/io-dispatcher/io_client.c new file +0
-0
[ ... ]
Quoted 2 lines
drivers/virt/bao/io-dispatcher/io_client.c new file +2
-0+static void bao_io_client_destroy (struct bao_io_client *client) +{
[ ... ]
drivers/virt/bao/io-dispatcher/io_client.c line 172
Quoted 12 lines
drivers/virt/bao/io-dispatcher/io_client.c new file +12
-0+/* Free any I/O requests still queued but never consumed. */ +mutex_lock (&client->virtio_requests_lock); +list_for_each_entry_safe (io_req, io_next, &client->virtio_requests, + list) { +list_del (&io_req->list); +kfree (io_req); + } + client->nr_requests =0 ; +mutex_unlock (&client->virtio_requests_lock); + +kfree (client); +}
[Severity: Medium]
Are we silently dropping pending I/O requests without notifying the hypervisor?
If a client is destroyed while it still has pending requests in the virtio_requests queue, these requests are freed via kfree() but are never completed back to the hypervisor.
Could this cause the frontend guest vCPU waiting for the synchronous completion to hang permanently?
Quoted 2 lines
drivers/virt/bao/io-dispatcher/io_dispatcher.c new file +0
-0
[ ... ]
Quoted 2 lines
drivers/virt/bao/io-dispatcher/io_dispatcher.c new file +2
-0+int bao_dispatch_io (struct bao_dm *dm) +{
[ ... ]
Quoted 24 lines
drivers/virt/bao/io-dispatcher/io_dispatcher.c new file +24
-0+down_read (&dm->io_clients_lock); + client =bao_io_client_find (dm, &req); +if (!client) { +up_read (&dm->io_clients_lock); +bao_io_request_complete_error (dm, &req); +return -ENODEV ; + } + +if (!bao_io_client_push_request (client, &req)) { +up_read (&dm->io_clients_lock); +bao_io_request_complete_error (dm, &req); +return -ENOMEM ; + } + +wake_up_interruptible (&client->wq); +up_read (&dm->io_clients_lock); + +return ctx.npend_req; +} + +/** +* io_dispatcher - Workqueue handler for dispatching I/O +* @work: Work struct representing this dispatch operation +*
drivers/virt/bao/io-dispatcher/io_dispatcher.c line 105
Quoted 10 lines
drivers/virt/bao/io-dispatcher/io_dispatcher.c new file +10
-0+* Handles all pending I/O requests for the associated Bao DM. +* Executed in process context by the workqueue. +*/ +static void io_dispatcher (struct work_struct *work) +{ + struct bao_dm *dm = container_of(work, struct bao_dm, io_work); + +while (bao_dispatch_io (dm) >0 ) +cpu_relax (); +}
[Severity: High]
Does this loop terminate prematurely if a single request fails to route?
When bao_dispatch_io() encounters an unroutable request or a full client queue, it returns a negative error code (-ENODEV or -ENOMEM) instead of the number of remaining pending requests (ctx.npend_req).
The while loop in io_dispatcher() terminates on a negative return value.
Since hypervisor notifications are edge-triggered, won't any remaining requests in the hypervisor's queue be stranded indefinitely?
Quoted 2 lines
drivers/virt/bao/io-dispatcher/ioeventfd.c new file +0
-0
[ ... ]
Quoted 21 lines
drivers/virt/bao/io-dispatcher/ioeventfd.c new file +21
-0+static bool bao_ioeventfd_is_conflict (struct bao_dm *dm, +struct ioeventfd *ioeventfd) +{ +struct ioeventfd *p; + +lockdep_assert_held (&dm->ioeventfds_lock); + +if (WARN_ON_ONCE (!dm || !ioeventfd)) +return true; + +list_for_each_entry (p, &dm->ioeventfds, list) { +if (p->eventfd == ioeventfd->eventfd && + p->addr == ioeventfd->addr && + (p->wildcard || ioeventfd->wildcard || + p->data == ioeventfd->data)) { +return true; + } + } + +return false; +}
[ ... ]
Quoted 5 lines
drivers/virt/bao/io-dispatcher/ioeventfd.c new file +5
-0+static int bao_ioeventfd_assign (struct bao_dm *dm,struct bao_ioeventfd *config) +{ +struct eventfd_ctx *eventfd; +struct ioeventfd *new; +int rc =0 ;
[ ... ]
drivers/virt/bao/io-dispatcher/ioeventfd.c line 168
Quoted 5 lines
drivers/virt/bao/io-dispatcher/ioeventfd.c new file +5
-0+ new =kzalloc_obj (*new,GFP_KERNEL ); +if (!new) { + rc = -ENOMEM ; + goto err_put_eventfd; + }
[Severity: High]
Could this allow an unbounded allocation of ioeventfd structures?
The BAO_IOCTL_IOEVENTFD ioctl dynamically allocates a new ioeventfd for each call. The conflict check in bao_ioeventfd_is_conflict() verifies the exact (eventfd, addr, data) tuple.
Would this allow an attacker to bypass file descriptor limits by repeatedly registering the exact same eventfd with different addresses, leading to unbounded kernel memory exhaustion and a potential denial of service?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
PATCH v4 3/3 MAINTAINERS: add Bao hypervisor entry
In reply to João PeixotoAdd a MAINTAINERS entry covering the Bao hypervisor drivers and their UAPI header.
---
v4:
- Drop the arch/*/include/asm/bao.h and include/linux/bao.h entries (the files are gone); use the directory form for drivers/virt/bao/.
MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+)
MAINTAINERS +8 -0
@@ -4559,6 +4559,14 @@ F: drivers/video/backlight/ F: include/linux/backlight.h F: include/linux/pwm_backlight.h +BAO HYPERVISOR +M: José Martins <[email protected]> +M: David Cerdeira <[email protected]> +M: João Peixoto <[email protected]> +S: Maintained +F: drivers/virt/bao/ +F: include/uapi/linux/bao.h + BARCO P50 GPIO DRIVER M: Santosh Kumar Yadav <[email protected]> M: Peter Korsgaard <[email protected]>
Signature · 1 line
2.43.0