firecracker-microvm/firecracker · error

GDB requires KVM

Error message

GDB requires KVM

What it means

`expect("GDB requires KVM")` at the top of `gdb_thread` (mod.rs:47): before serving any GDB connection, the stub locks the Vmm and calls `vmm.vm.as_kvm()` to obtain vcpu fds for installing the entry hardware breakpoint. If the VM is not KVM-backed, as_kvm() is None and the thread panics.

Source

Thrown at src/vmm/src/gdb/mod.rs:47

///
/// This will then create the GDB socket which will be used for communication to the GDB process.
/// After creating this, the function will block while waiting for GDB to connect.
///
/// After the connection has been established the function will start a new thread for handling
/// communcation to the GDB server
pub fn gdb_thread(
    vmm: Arc<Mutex<Vmm>>,
    gdb_event_receiver: Receiver<usize>,
    entry_addr: GuestAddress,
    socket_addr: &str,
) -> Result<(), GdbTargetError> {
    // We register a hw breakpoint at the entry point as GDB expects the application
    // to be stopped as it connects. This also allows us to set breakpoints before kernel starts.
    // This entry adddress is automatically used as it is not tracked inside the target state, so
    // when resumed will be removed
    {
        let vmm = vmm.lock().unwrap();
        let kvm_vm = vmm.vm.as_kvm().expect("GDB requires KVM");
        let handles = kvm_vm.vcpus_handles();
        vcpu_set_debug(&handles[0].vcpu_fd, &[entry_addr], false)?;
        for handle in &handles[1..] {
            vcpu_set_debug(&handle.vcpu_fd, &[], false)?;
        }
    }

    let path = Path::new(socket_addr);
    let listener = UnixListener::bind(path).map_err(GdbTargetError::ServerSocketError)?;
    trace!("Waiting for GDB server connection on {}...", path.display());
    let (connection, _addr) = listener
        .accept()
        .map_err(GdbTargetError::ServerSocketError)?;

    std::thread::Builder::new()
        .name("gdb".into())
        .spawn(move || event_loop(connection, vmm, gdb_event_receiver, entry_addr))
        .map_err(|_| GdbTargetError::GdbThreadError)?;

View on GitHub (pinned to 0a745def42)

Solutions

  1. Verify KVM is available and used before spawning the gdb thread: check `vmm.vm.as_kvm().is_some()` and return an error like RequiresKvm otherwise
  2. Ensure /dev/kvm is present and the user has rw access (kvm group)
  3. Skip enabling the GDB feature on non-KVM deployments

Example fix

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

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

Strategy: validation

Validate before calling

// Pre-flight the gdb thread
let kvm_ok = vmm.lock().unwrap().vm.as_kvm().is_some();
if !kvm_ok {
    return Err(GdbTargetError::RequiresKvm); // instead of spawning gdb_thread
}

Type guard

fn gdb_supported(vmm: &Arc<Mutex<Vmm>>) -> bool {
    vmm.lock().unwrap().vm.as_kvm().is_some()
}

Prevention

When it happens

Trigger: Starting the GDB thread (`gdb_thread(...)`) against a Vmm constructed with a non-KVM backend — the breakpoint-install step `vcpu_set_debug(&handles[0].vcpu_fd, &[entry_addr], false)` needs real KVM vcpu fds and there is no fallback.

Common situations: GDB/debug feature enabled in a build or test environment where the VM runs without KVM (mock vm in tests, alternate hypervisor backend, /dev/kvm absent so a degraded config was picked).

Related errors


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