firecracker-microvm/firecracker · error

GDB requires KVM

Error message

GDB requires KVM

What it means

`expect("GDB requires KVM")` in the AArch64 GDB support (arch/aarch64.rs:68): `vmm.vm.as_kvm()` returns None when the Vmm was not built on a KVM-backed VM, and GDB stub memory access (read_address) has no fallback. Firecracker's GDB implementation is written exclusively against the KVM API (vcpu fds, KVM translations), hence the hard requirement.

Source

Thrown at src/vmm/src/gdb/arch/aarch64.rs:68

    get_sys_reg(PC_REG_ID, vcpu_fd)
}

/// Helper to extract a specific number of bits at an offset from a u64
macro_rules! extract_bits_64 {
    ($value: tt, $offset: tt, $length: tt) => {
        ($value >> $offset) & (!0u64 >> (64 - $length))
    };
}

/// Mask to clear the last 3 bits from the page table entry
const PTE_ADDRESS_MASK: u64 = !0b111u64;

/// Read a u64 value from a guest memory address
fn read_address(vmm: &Vmm, address: u64) -> Result<u64, GdbTargetError> {
    let mut buf = [0; 8];
    vmm.vm
        .as_kvm()
        .expect("GDB requires KVM")
        .guest_memory()
        .read(&mut buf, GuestAddress(address))?;

    Ok(u64::from_le_bytes(buf))
}

/// The grainsize used with 4KB paging
const GRAIN_SIZE: usize = 9;

/// Translates a virtual address according to the Vcpu's current address translation mode.
/// Returns the GPA (guest physical address)
///
/// To simplify the implementation we've made some assumptions about the paging setup.
/// Here we just assert firstly paging is setup and these assumptions are correct.
pub fn translate_gva(vcpu_fd: &VcpuFd, gva: u64, vmm: &Vmm) -> Result<u64, GdbTargetError> {
    // Check this virtual address is in kernel space
    if extract_bits_64!(gva, 55, 1) == 0 {
        return Err(GdbTargetError::GvaTranslateError);

View on GitHub (pinned to 0a745def42)

Solutions

  1. Run the microvm with the KVM backend and confirm /dev/kvm exists and is accessible (rw for the user/kvm group)
  2. Gate the GDB feature: refuse to start the gdb thread unless `vmm.vm.as_kvm().is_some()`
  3. In code, convert the expect into an error: `vmm.vm.as_kvm().ok_or(GdbTargetError::RequiresKvm)?`

Example fix

// before
vmm.vm
    .as_kvm()
    .expect("GDB requires KVM")
    .guest_memory()
    .read(&mut buf, GuestAddress(address))?;

// after
let kvm = vmm.vm.as_kvm().ok_or(GdbTargetError::RequiresKvm)?;
kvm.guest_memory().read(&mut buf, GuestAddress(address))?;
Defensive patterns

Strategy: validation

Validate before calling

// Enable the gdb feature only on KVM-backed microvms
if cfg!(feature = "gdb") && vmm.vm.as_kvm().is_none() {
    return Err(GdbTargetError::RequiresKvm);
}

Type guard

fn is_kvm_backed(vmm: &Vmm) -> bool {
    vmm.vm.as_kvm().is_some()
}

Prevention

When it happens

Trigger: Enabling the GDB socket (`--gdb`/api config) and issuing a memory read (GDB `x`/`m` packet) on a Vmm whose `vm` wrapper is not a KVM Vm — alternate hypervisor backends, or test harnesses constructing Vmm with a mock/non-KVM vm object.

Common situations: Running the VMM under a non-KVM backend or in unit tests with fake Vm types while the gdb feature is compiled in; platforms where /dev/kvm is unavailable so a degraded backend was selected.

Related errors


AI-assisted analysis of firecracker-microvm/firecracker@0a745def42 (2026-08-19). Data as JSON: /api/errors/8506a4cc004f6007. Report an issue: GitHub.