Skip to content

Commit 8ff4adf

Browse files
committed
harden(virtio-mmio): preserve cumulative status bits across transitions
Backports the MMIO portion of upstream PR firecracker-microvm#5818 (commit 4a52198). Replaces the match on `!self.device_status & status` with an explicit VALID_TRANSITIONS table and equality check, so writes that drop previously-set bits (e.g. FEATURES_OK alone after the device is in state ACK | DRIVER | FEATURES_OK | DRIVER_OK) are rejected. This is *not* a fix for CVE-2026-5747. Per AWS security bulletin 2026-015, that CVE is specific to the virtio PCI transport, which is opt-in via --enable-pci and was added upstream in v1.13.0. Our fork is based on v1.6.5 and has no PCI transport, so the CVE itself does not apply. Upstream's PR firecracker-microvm#5818 included a parenthetical defensive hardening of the MMIO transport ("Note: virtio MMIO transport also didn't [enforce cumulative bits]") and that is what this commit backports — for consistency with upstream's stricter behaviour and defence-in-depth, not because of an active CVE. The upstream commit could not be cherry-picked cleanly because by v1.15.x the mmio transport lives at src/vmm/src/devices/virtio/ transport/mmio.rs, the activate() signature gained an interrupt argument, and the failure path uses DEVICE_NEEDS_RESET / VirtioInterruptType, none of which exist in v1.6.5. The control flow and the VALID_TRANSITIONS contents are deliberately kept identical to upstream so future audits can compare line-for-line.
1 parent 138c9f9 commit 8ff4adf

1 file changed

Lines changed: 43 additions & 40 deletions

File tree

src/vmm/src/devices/virtio/mmio.rs

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -172,55 +172,58 @@ impl MmioTransport {
172172
#[allow(unused_assignments)]
173173
fn set_device_status(&mut self, status: u32) {
174174
use device_status::*;
175-
// match changed bits
176-
match !self.device_status & status {
177-
ACKNOWLEDGE if self.device_status == INIT => {
178-
self.device_status = status;
179-
}
180-
DRIVER if self.device_status == ACKNOWLEDGE => {
181-
self.device_status = status;
175+
176+
const VALID_TRANSITIONS: &[(u32, u32)] = &[
177+
(INIT, ACKNOWLEDGE),
178+
(ACKNOWLEDGE, ACKNOWLEDGE | DRIVER),
179+
(ACKNOWLEDGE | DRIVER, ACKNOWLEDGE | DRIVER | FEATURES_OK),
180+
(
181+
ACKNOWLEDGE | DRIVER | FEATURES_OK,
182+
ACKNOWLEDGE | DRIVER | FEATURES_OK | DRIVER_OK,
183+
),
184+
];
185+
186+
if (status & FAILED) != 0 {
187+
// TODO: notify backend driver to stop the device
188+
self.device_status |= FAILED;
189+
} else if status == INIT {
190+
if self.locked_device().is_activated() {
191+
let mut device_status = self.device_status;
192+
let reset_result = self.locked_device().reset();
193+
match reset_result {
194+
Some((_interrupt_evt, mut _queue_evts)) => {}
195+
None => {
196+
device_status |= FAILED;
197+
}
198+
}
199+
self.device_status = device_status;
182200
}
183-
FEATURES_OK if self.device_status == (ACKNOWLEDGE | DRIVER) => {
184-
self.device_status = status;
201+
202+
// If the backend device driver doesn't support reset,
203+
// just leave the device marked as FAILED.
204+
if self.device_status & FAILED == 0 {
205+
self.reset();
185206
}
186-
DRIVER_OK if self.device_status == (ACKNOWLEDGE | DRIVER | FEATURES_OK) => {
187-
self.device_status = status;
207+
} else if VALID_TRANSITIONS
208+
.iter()
209+
.any(|&(from, to)| self.device_status == from && status == to)
210+
{
211+
self.device_status = status;
212+
213+
// Activate the device when transitioning to DRIVER_OK.
214+
if status == (ACKNOWLEDGE | DRIVER | FEATURES_OK | DRIVER_OK) {
188215
let device_activated = self.locked_device().is_activated();
189216
if !device_activated && self.are_queues_valid() {
190217
self.locked_device()
191218
.activate(self.mem.clone())
192219
.expect("Failed to activate device");
193220
}
194221
}
195-
_ if (status & FAILED) != 0 => {
196-
// TODO: notify backend driver to stop the device
197-
self.device_status |= FAILED;
198-
}
199-
_ if status == 0 => {
200-
if self.locked_device().is_activated() {
201-
let mut device_status = self.device_status;
202-
let reset_result = self.locked_device().reset();
203-
match reset_result {
204-
Some((_interrupt_evt, mut _queue_evts)) => {}
205-
None => {
206-
device_status |= FAILED;
207-
}
208-
}
209-
self.device_status = device_status;
210-
}
211-
212-
// If the backend device driver doesn't support reset,
213-
// just leave the device marked as FAILED.
214-
if self.device_status & FAILED == 0 {
215-
self.reset();
216-
}
217-
}
218-
_ => {
219-
warn!(
220-
"invalid virtio driver status transition: 0x{:x} -> 0x{:x}",
221-
self.device_status, status
222-
);
223-
}
222+
} else {
223+
warn!(
224+
"invalid virtio driver status transition: {:#x} -> {:#x}",
225+
self.device_status, status
226+
);
224227
}
225228
}
226229
}

0 commit comments

Comments
 (0)