{"record":{"id":"5c13d9e831288a6e","repo":"firecracker-microvm/firecracker","slug":"i8042-lock-was-poisoned","errorCode":null,"errorMessage":"i8042 lock was poisoned","messagePattern":"i8042 lock was poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/vmm/src/lib.rs","lineNumber":494,"sourceCode":"        let kvm_vm = self\n            .vm\n            .as_kvm()\n            .ok_or_else(|| VmmError::NotSupportedOnVmType(self.vm.type_name()))?;\n        kvm_vm.pause_vcpus()?;\n        self.instance_info.state = VmState::Paused;\n        Ok(())\n    }\n\n    /// Injects CTRL+ALT+DEL keystroke combo in the i8042 device.\n    #[cfg(target_arch = \"x86_64\")]\n    pub fn send_ctrl_alt_del(&mut self) -> Result<(), VmmError> {\n        self.device_manager\n            .legacy_devices\n            .as_ref()\n            .ok_or(VmmError::NotSupported)?\n            .i8042\n            .lock()\n            .expect(\"i8042 lock was poisoned\")\n            .trigger_ctrl_alt_del()\n            .map_err(VmmError::I8042Error)\n    }\n\n    /// Saves the state of a paused Microvm.\n    pub fn save_state(&mut self, vm_info: &VmInfo) -> Result<MicrovmState, MicrovmStateError> {\n        self.check_unsnapshottable_devices()?;\n\n        // We need to save device state before saving KVM state.\n        // Some devices, (at the time of writing this comment block device with async engine)\n        // might modify the VirtIO transport and send an interrupt to the guest. If we save KVM\n        // state before we save device state, that interrupt will never be delivered to the guest\n        // upon resuming from the snapshot.\n        let device_states = self.device_manager.save();\n        let kvm_vm = self\n            .vm\n            .as_kvm()\n            .ok_or_else(|| MicrovmStateError::NotAllowed(\"save_state requires KVM\".into()))?;","sourceCodeStart":476,"sourceCodeEnd":512,"githubUrl":"https://github.com/firecracker-microvm/firecracker/blob/0a745def42ddf4cc2a744d79a08a27ff50b5d27a/src/vmm/src/lib.rs#L476-L512","documentation":"Panic in `Vmm::send_ctrl_alt_del` (lib.rs:494, x86_64 only): the i8042 (keyboard controller) device mutex is acquired with `.lock().expect(\"i8042 lock was poisoned\")` before `trigger_ctrl_alt_del()`. Poisoned means another thread panicked while holding the i8042 lock; this API call then aborts the VMM instead of returning a VmmError (the function is already fallible — NotSupported, I8042Error).","triggerScenarios":"Calling the SendCtrlAltDel API endpoint after an earlier panic in whatever thread drives the i8042 device (input event handling / KVM exit handling holding the lock at panic time).","commonSituations":"Attempting a keyboard-reset style reboot of the guest after an input-device thread crashed; restoring/reusing a long-running firecracker process where the i8042 lock got poisoned by an unrelated panic while the legacy device was locked.","solutions":["Locate the original panic in the logs (first occurrence, before this expect) and fix it — poisoning is only a symptom","Map the poisoning to a VmmError instead of panicking: `.lock().map_err(|_| VmmError::I8042Poisoned)` or unwrap_or_else(into_inner) if reset-on-poison is acceptable","Review i8042 lock scopes: never hold the lock across infallible-looking code that can panic (e.g. indexing, unwraps in event handlers)"],"exampleFix":"// before\nself.device_manager\n    .legacy_devices\n    .as_ref()\n    .ok_or(VmmError::NotSupported)?\n    .i8042\n    .lock()\n    .expect(\"i8042 lock was poisoned\")\n    .trigger_ctrl_alt_del()\n    .map_err(VmmError::I8042Error)\n\n// after\nself.device_manager\n    .legacy_devices\n    .as_ref()\n    .ok_or(VmmError::NotSupported)?\n    .i8042\n    .lock()\n    .unwrap_or_else(|poisoned| poisoned.into_inner())\n    .trigger_ctrl_alt_del()\n    .map_err(VmmError::I8042Error)","handlingStrategy":"fallback","validationCode":"// Nothing prevents the panic at the call site; instead check service health before sending CAD\nif i8042_is_healthy() {\n    vmm.send_ctrl_alt_del()?;\n} else {\n    // fall back to a different reset mechanism (e.g. action_type REBOOT if available)\n}","typeGuard":null,"tryCatchPattern":"// Recover the lock since trigger_ctrl_alt_del only needs the input state\nlet i8042 = self.device_manager\n    .legacy_devices\n    .as_ref()\n    .ok_or(VmmError::NotSupported)?\n    .i8042\n    .lock()\n    .unwrap_or_else(|p| p.into_inner());\ni8042.trigger_ctrl_alt_del().map_err(VmmError::I8042Error)","preventionTips":["Keep i8042 lock critical sections panic-free (no unwraps/indexing while locked)","React to the FIRST panic that poisons it — this expect is downstream noise","Have a non-keyboard reset fallback (VM reboot action) for CAD delivery failures"],"tags":["rust","panic","mutex","poisoned-lock","i8042","input","reboot","firecracker"],"backgroundTag":"mutex-poisoned","analyzedSha":"0a745def42ddf4cc2a744d79a08a27ff50b5d27a","analyzedAt":"2026-08-19T05:27:02.517Z","contentChangedAt":"2026-08-19T05:27:02.517Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}