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
- Before creating the target, validate the backend: `if vmm.vm.as_kvm().is_none() { return Err(GdbTargetError::RequiresKvm) }`
- Run the microvm with KVM (check /dev/kvm permissions, kvm group membership)
- 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
- Construct the target only after the VM is confirmed KVM-backed
- Pass vcpus_count explicitly from a validated source instead of re-deriving it inside new()
- Unit-test FirecrackerTarget::new against a KVM-enabled test VM in CI
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
- GDB requires KVM
- GDB requires KVM
- Error getting initial gdb event
- Error converting cpu id to Tid
- error unlocking vmm
AI-assisted analysis of firecracker-microvm/firecracker@0a745def42 (2026-08-19).
Data as JSON: /api/errors/1dea175889a7e39b.
Report an issue: GitHub.