firecracker-microvm/firecracker · error

GDB requires KVM

Error message

GDB requires KVM

What it means

`expect("GDB requires KVM")` in `FirecrackerTarget::new` (target.rs:172): building the GDB target locks the Vmm, calls `vmm.vm.as_kvm()` and counts vcpus via `kvm_vm.vcpus_handles().len()` to size `vcpu_state`. A non-KVM VM wrapper yields None and construction panics.

Source

Thrown at src/vmm/src/gdb/target.rs:172

/// the Tid required by GDB
pub fn vcpuid_to_tid(cpu_id: usize) -> Result<Tid, GdbTargetError> {
    Tid::new(get_raw_tid(cpu_id)).ok_or(GdbTargetError::TidConversionError)
}

/// Converts the inernal index of a Vcpu to
/// the 1 indexed value for GDB
pub fn get_raw_tid(cpu_id: usize) -> usize {
    cpu_id + 1
}

impl FirecrackerTarget {
    /// Creates a new Target for GDB stub. This is used as the layer between GDB and the VMM it
    /// will handle requests from GDB and perform the appropriate actions, while also updating GDB
    /// with the state of the VMM / Vcpu's as we hit debug events
    pub fn new(vmm: Arc<Mutex<Vmm>>, gdb_event: Receiver<usize>, entry_addr: GuestAddress) -> Self {
        let vcpus_count = {
            let vmm = vmm.lock().unwrap();
            let kvm_vm = vmm.vm.as_kvm().expect("GDB requires KVM");
            kvm_vm.vcpus_handles().len()
        };
        let mut vcpu_state = vec![VcpuState::default(); vcpus_count];
        // By default vcpu 1 will be paused at the entry point
        vcpu_state[0].paused = true;

        Self {
            vmm,
            entry_addr,
            gdb_event,
            // We only support 4 hw breakpoints on x86 this will need to be configurable on arm
            hw_breakpoints: Default::default(),
            sw_breakpoints: HashMap::new(),
            vcpu_state,

            paused_vcpu: Tid::new(1),
            scheduler_locking: false,
        }

View on GitHub (pinned to 0a745def42)

Solutions

  1. Before creating the target, validate the backend: `if vmm.vm.as_kvm().is_none() { return Err(GdbTargetError::RequiresKvm) }`
  2. Run the microvm with KVM (check /dev/kvm permissions, kvm group membership)
  3. Disable the gdb option in deployments that intentionally use a non-KVM backend

Example fix

// before
let kvm_vm = vmm.vm.as_kvm().expect("GDB requires KVM");
let vcpus_count = kvm_vm.vcpus_handles().len();

// after
let vcpus_count = vmm
    .vm
    .as_kvm()
    .ok_or(GdbTargetError::RequiresKvm)?
    .vcpus_handles()
    .len();
Defensive patterns

Strategy: validation

Validate before calling

if vmm.lock().unwrap().vm.as_kvm().is_none() {
    return Err(GdbTargetError::RequiresKvm);
}
let target = FirecrackerTarget::new(vmm.clone(), rx, entry_addr);

Type guard

fn can_build_gdb_target(vmm: &Arc<Mutex<Vmm>>) -> bool {
    vmm.lock().map(|v| v.vm.as_kvm().is_some()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Constructing `FirecrackerTarget` (i.e. any gdb session start) when the Vmm's vm is not a KVM vm — the vcpu-handle enumeration this code depends on exists only on the KVM implementation.

Common situations: GDB feature turned on for a microvm launched without the KVM backend; tests instantiating FirecrackerTarget with a stubbed Vmm/Vm; running on hosts without /dev/kvm where the VM fell back to a non-KVM path.

Related errors


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