RT-Thread Version
master at commit 2b58dec. Also on released tags v5.0.0, v5.0.2, v5.1.0, v5.2.0, v5.2.1, and v5.2.2.
Hardware Type/Architectures
Any RT-Thread guest platform enabling virtio drivers
Develop Toolchain
GCC
Describe the bug
Summary
Multiple RT-Thread virtio guest drivers unconditionally trust device-controlled used-ring metadata and use it directly as local array indices or derived copy lengths without any validation. A malicious or compromised virtio backend can inject forged completion entries across the guest/host trust boundary and trigger out-of-bounds memory accesses inside the guest kernel.
This is not a crash-only robustness issue. Because the virtio backend controls the timing and content of queue completion metadata, a compromised host-side backend has a direct and reliable host-to-guest kernel memory corruption primitive against any RT-Thread guest running these drivers.
Affected Components
| Driver |
File |
Vulnerable Function |
Nature of Bug |
| virtio-input |
components/drivers/virtio/virtio_input.c |
virtio_input_isr() |
Backend-controlled id indexes fixed guest arrays |
| virtio-console |
components/drivers/virtio/virtio_console.c |
virtio_console_isr() |
Backend-controlled id indexes info[id] |
| virtio-gpu |
components/drivers/virtio/virtio_gpu.c |
virtio_gpu_isr() |
Backend-controlled id indexes info[id] |
| virtio-blk |
components/drivers/virtio/virtio_blk.c |
virtio_blk_isr() |
Backend-controlled id indexes info[id] |
| virtio-net |
components/drivers/virtio/virtio_net.c |
virtio_net_rx() |
Backend-controlled len underflows and drives OOB copy |
Details
Pattern 1: Unvalidated Used-Ring ID as Array Index
In virtio_input_isr(), the driver reads event_queue->used->ring[..].id directly from shared virtqueue memory and immediately uses it to index fixed-size guest arrays:
virtio_input.c:300
virtio_input.c:305
virtio_input.c:306
The destination arrays are fixed-size, defined in:
virtio_input.h:21
virtio_input.h:120
virtio_input.h:123
There is no check that id < queue->num, and no check that id < ARRAY_SIZE(recv_events) or id < ARRAY_SIZE(bcst_events). A backend-supplied id of any out-of-range value directly corrupts guest kernel memory.
The identical bug pattern is present in:
virtio_console_isr() — backend-controlled id used to access virtio_console_dev->info[id]
virtio_gpu_isr() — backend-controlled id used to access virtio_gpu_dev->info[id]
virtio_blk_isr() — backend-controlled id used to access virtio_blk_dev->info[id]
Pattern 2: Used-Length Underflow in virtio-net RX Path
In virtio_net_rx() (virtio_net.c:69–82), the driver derives a payload copy length as:
id = (used.id + 1) % queue_rx->num;
len = used.len - VIRTIO_NET_HDR_SIZE;
If the backend returns used.len < VIRTIO_NET_HDR_SIZE, the subtraction underflows in unsigned arithmetic, producing a very large len. This underflowed value is then only clamped against VIRTIO_NET_PAYLOAD_MAX_SIZE — not against the actual RX descriptor size (VIRTIO_NET_MSS, defined in virtio_net.h:79–81).
The driver then executes:
rt_memcpy(p->payload, (void *)queue_rx->desc[id].addr - PV_OFFSET, len);
with the attacker-controlled len, creating a guest-kernel out-of-bounds read primitive in the network RX path.
Why This Is High Severity
The virtio used ring is shared memory between the guest and the backend. In any deployment where the virtio backend is not in the same trust domain as the guest — including cloud virtualization, paravirtualized containers, or any scenario with a potentially compromised host — the backend's control over completion metadata constitutes a real cross-trust-boundary attack surface. There is no hardware enforcement preventing a malicious backend from forging id or len values in shared ring memory.
Steps to Reproduce
PoC 1: virtio_input_isr() Out-of-Bounds Index
- Build an RT-Thread guest with virtio-input enabled
- Use a custom virtio input backend, instrumented hypervisor, or modified QEMU device model
- After guest queue initialization, write a forged used-ring completion into the input event queue:
id = 64 (or any value >= VIRTIO_INPUT_EVENT_QUEUE_SIZE)
len = sizeof(struct virtio_input_event)
- Trigger the virtio interrupt
- The guest enters
virtio_input_isr() and uses the forged id to index recv_events[id] and bcst_events[id], causing an out-of-bounds guest-kernel memory access
The same malformed-used-ring-id technique reaches the equivalent info[id] accesses in virtio_console, virtio_gpu, and virtio_blk.
PoC 2: virtio_net_rx() Used-Length Underflow
- Build an RT-Thread guest with virtio-net enabled
- In the virtio net backend, return a forged RX used-ring entry:
used.id = 0
used.len = 0 (or any value < VIRTIO_NET_HDR_SIZE)
- Trigger the RX interrupt
- The guest computes
len = used.len - VIRTIO_NET_HDR_SIZE — unsigned underflow
- The resulting oversized
len drives rt_memcpy(), producing an out-of-bounds read from the RX descriptor area
PoC 3: virtio_console_isr() Control Queue ID Corruption
- Build an RT-Thread guest with virtio-console enabled
- In the control RX queue backend, return:
id >= VIRTIO_CONSOLE_QUEUE_SIZE
len = sizeof(struct virtio_console_control)
- Trigger the interrupt
- The guest reaches
virtio_console_dev->info[id].rx_ctrl — out-of-bounds guest-kernel access
Expected Behavior
RT-Thread virtio guest drivers must treat all used-ring metadata — id, len, and derived values — as untrusted device input from outside the guest trust boundary. Every completion entry must be validated before any local array access or memory copy operation. Invalid id or len values must result in a logged error and safe discard or reset, never in out-of-bounds guest-kernel memory access.
Fix Suggestion
Validate Used-Ring ID Before Any Array Access
In virtio_input.c, virtio_console.c, virtio_gpu.c, and virtio_blk.c, add bounds checks before every info[id] or event-array dereference:
if (id >= queue->num || id >= ARRAY_SIZE(driver_local_array)) {
LOG_E("virtio: malformed used-ring id %u, discarding", id);
continue; /* or break/reset as appropriate */
}
Fix Used-Length Underflow in virtio_net_rx()
Before computing len, validate used.len:
if (used.len < VIRTIO_NET_HDR_SIZE) {
LOG_E("virtio-net: used.len %u < header size, discarding RX entry", used.len);
continue;
}
len = used.len - VIRTIO_NET_HDR_SIZE;
if (len > VIRTIO_NET_MSS) {
LOG_E("virtio-net: derived payload len %u > MSS, discarding", len);
continue;
}
Centralize Validation in a Shared Helper
Add a shared validation routine in components/drivers/virtio/virtio.c or a common header so all virtio drivers use one centralized path for used-ring id and len validation, preventing future recurrence across the driver family.
Impact
This is a host-to-guest kernel memory corruption vulnerability affecting all RT-Thread guests that enable any of the five identified virtio drivers.
Impact includes:
- Out-of-bounds write in guest kernel driver state via forged used-ring
id (virtio-input, virtio-console, virtio-gpu, virtio-blk)
- Out-of-bounds read in guest kernel RX path via forged used-ring
len underflow (virtio-net)
- Denial of service / guest kernel crash
- Corruption of guest kernel driver control structures
- In adversarial deployments: reliable cross-trust-boundary host-to-guest kernel memory corruption primitive
Who is impacted:
- Any RT-Thread deployment running as a virtio guest (QEMU, KVM, cloud VM, or paravirtualized environment)
- Deployments where the virtio backend is in a separate trust domain from the guest
Kindly let me know if you intend to request a CVE ID upon confirmation of the vulnerability.
Other additional context
No response
RT-Thread Version
master at commit 2b58dec. Also on released tags v5.0.0, v5.0.2, v5.1.0, v5.2.0, v5.2.1, and v5.2.2.
Hardware Type/Architectures
Any RT-Thread guest platform enabling virtio drivers
Develop Toolchain
GCC
Describe the bug
Summary
Multiple RT-Thread virtio guest drivers unconditionally trust device-controlled used-ring metadata and use it directly as local array indices or derived copy lengths without any validation. A malicious or compromised virtio backend can inject forged completion entries across the guest/host trust boundary and trigger out-of-bounds memory accesses inside the guest kernel.
This is not a crash-only robustness issue. Because the virtio backend controls the timing and content of queue completion metadata, a compromised host-side backend has a direct and reliable host-to-guest kernel memory corruption primitive against any RT-Thread guest running these drivers.
Affected Components
components/drivers/virtio/virtio_input.cvirtio_input_isr()idindexes fixed guest arrayscomponents/drivers/virtio/virtio_console.cvirtio_console_isr()idindexesinfo[id]components/drivers/virtio/virtio_gpu.cvirtio_gpu_isr()idindexesinfo[id]components/drivers/virtio/virtio_blk.cvirtio_blk_isr()idindexesinfo[id]components/drivers/virtio/virtio_net.cvirtio_net_rx()lenunderflows and drives OOB copyDetails
Pattern 1: Unvalidated Used-Ring ID as Array Index
In
virtio_input_isr(), the driver readsevent_queue->used->ring[..].iddirectly from shared virtqueue memory and immediately uses it to index fixed-size guest arrays:virtio_input.c:300virtio_input.c:305virtio_input.c:306The destination arrays are fixed-size, defined in:
virtio_input.h:21virtio_input.h:120virtio_input.h:123There is no check that
id < queue->num, and no check thatid < ARRAY_SIZE(recv_events)orid < ARRAY_SIZE(bcst_events). A backend-suppliedidof any out-of-range value directly corrupts guest kernel memory.The identical bug pattern is present in:
virtio_console_isr()— backend-controlledidused to accessvirtio_console_dev->info[id]virtio_gpu_isr()— backend-controlledidused to accessvirtio_gpu_dev->info[id]virtio_blk_isr()— backend-controlledidused to accessvirtio_blk_dev->info[id]Pattern 2: Used-Length Underflow in virtio-net RX Path
In
virtio_net_rx()(virtio_net.c:69–82), the driver derives a payload copy length as:If the backend returns
used.len < VIRTIO_NET_HDR_SIZE, the subtraction underflows in unsigned arithmetic, producing a very largelen. This underflowed value is then only clamped againstVIRTIO_NET_PAYLOAD_MAX_SIZE— not against the actual RX descriptor size (VIRTIO_NET_MSS, defined invirtio_net.h:79–81).The driver then executes:
with the attacker-controlled
len, creating a guest-kernel out-of-bounds read primitive in the network RX path.Why This Is High Severity
The virtio used ring is shared memory between the guest and the backend. In any deployment where the virtio backend is not in the same trust domain as the guest — including cloud virtualization, paravirtualized containers, or any scenario with a potentially compromised host — the backend's control over completion metadata constitutes a real cross-trust-boundary attack surface. There is no hardware enforcement preventing a malicious backend from forging
idorlenvalues in shared ring memory.Steps to Reproduce
PoC 1:
virtio_input_isr()Out-of-Bounds Indexid = 64(or any value>= VIRTIO_INPUT_EVENT_QUEUE_SIZE)len = sizeof(struct virtio_input_event)virtio_input_isr()and uses the forgedidto indexrecv_events[id]andbcst_events[id], causing an out-of-bounds guest-kernel memory accessThe same malformed-used-ring-id technique reaches the equivalent
info[id]accesses invirtio_console,virtio_gpu, andvirtio_blk.PoC 2:
virtio_net_rx()Used-Length Underflowused.id = 0used.len = 0(or any value< VIRTIO_NET_HDR_SIZE)len = used.len - VIRTIO_NET_HDR_SIZE— unsigned underflowlendrivesrt_memcpy(), producing an out-of-bounds read from the RX descriptor areaPoC 3:
virtio_console_isr()Control Queue ID Corruptionid >= VIRTIO_CONSOLE_QUEUE_SIZElen = sizeof(struct virtio_console_control)virtio_console_dev->info[id].rx_ctrl— out-of-bounds guest-kernel accessExpected Behavior
RT-Thread virtio guest drivers must treat all used-ring metadata —
id,len, and derived values — as untrusted device input from outside the guest trust boundary. Every completion entry must be validated before any local array access or memory copy operation. Invalididorlenvalues must result in a logged error and safe discard or reset, never in out-of-bounds guest-kernel memory access.Fix Suggestion
Validate Used-Ring ID Before Any Array Access
In
virtio_input.c,virtio_console.c,virtio_gpu.c, andvirtio_blk.c, add bounds checks before everyinfo[id]or event-array dereference:Fix Used-Length Underflow in
virtio_net_rx()Before computing
len, validateused.len:Centralize Validation in a Shared Helper
Add a shared validation routine in
components/drivers/virtio/virtio.cor a common header so all virtio drivers use one centralized path for used-ringidandlenvalidation, preventing future recurrence across the driver family.Impact
This is a host-to-guest kernel memory corruption vulnerability affecting all RT-Thread guests that enable any of the five identified virtio drivers.
Impact includes:
id(virtio-input, virtio-console, virtio-gpu, virtio-blk)lenunderflow (virtio-net)Who is impacted:
Kindly let me know if you intend to request a CVE ID upon confirmation of the vulnerability.
Other additional context
No response