Signals, Shutdown, and Reset
A microVM can end in several ways: the guest can reboot, the guest can power off, the guest kernel
can issue an instruction that KVM reports as a shutdown, the VMM can fault (SIGSEGV/SIGBUS), the
VMM can attempt a forbidden syscall (SIGSYS from seccomp), or the operator can kill the process.
Firecracker has to turn each of these into a correct outcome — a clean exit, a guest reset, or a
fast fail-closed crash — without leaving the host, the jail, or the threads in a bad state. This
lifecycle path is small but security-critical: it's where the "fail closed" half of the threat model
is implemented.
This chapter covers the signal handler (SIGSYS/SIGBUS/SIGSEGV and fault handling), how a guest
reboot (i8042 / SendCtrlAltDel) or shutdown (KVM_EXIT_SHUTDOWN) propagates out to a process exit,
the orderly teardown of the VMM threads, and how the jailer cleans up after the process is gone.
Note: Firecracker's default posture is fail closed: when something goes wrong that it cannot safely continue past — a denied syscall, a memory fault in the VMM — it logs and exits rather than trying to recover. A VMM that limps along after a fault is a security liability. Crash-and-be-gone is the correct behavior for a process whose job is to isolate hostile guests.
The signal handler
rg -n "fn register_signal_handlers|sigaction|SIGSYS|SIGBUS|SIGSEGV|SIGPIPE|signal_handler|si_signo|si_addr" src/vmm/src/signal_handler.rs
find src/vmm/src -name "signal_handler.rs"
src/vmm/src/signal_handler.rs installs handlers for the signals Firecracker cares about. The
important ones:
| Signal | Source | Handler behavior |
|---|---|---|
SIGSYS | seccomp SECCOMP_RET_TRAP on a denied syscall | log the offending syscall number + thread, increment seccomp.num_faults, exit |
SIGBUS | bad memory access — classically a guest-memory-file I/O error / truncated mmap | log, increment signals.sigbus, exit |
SIGSEGV | a segmentation fault in the VMM | log, increment signals.sigsegv, exit |
SIGPIPE | writing to a closed pipe/socket (e.g. the API or log FIFO) | handled so it doesn't kill the process unexpectedly |
The handlers are deliberately minimal — signal handlers run in a constrained async-signal-safe
context, so they record what happened (to the metrics counters and the log)
and then terminate. SIGSYS is the one to internalize: it is the visible manifestation of the
seccomp boundary doing its job (see seccomp-filtering.md). When you see a VMM
exit with a SIGSYS/"bad syscall" log, that is the sandbox working, not a random crash.
# The SIGSYS handler reads the offending syscall from the siginfo.
rg -n "si_syscall|SYS_SECCOMP|seccomp.*fault|bad syscall" src/vmm/src/signal_handler.rs src/vmm/src/
Guest-initiated reset: i8042 and SendCtrlAltDel
rg -n "i8042|I8042|reset|0x64|ctrl_alt_del|CtrlAltDel|SendCtrlAltDel|reboot" src/vmm/src/devices/
Firecracker emulates only a partial i8042 keyboard controller — just enough to detect a guest
reset. When the guest kernel reboots (e.g. reboot=k cmdline makes the kernel use the keyboard
controller reset), it writes the reset command to the i8042 port; Firecracker's partial i8042
catches that write and turns it into a VMM-level "the guest wants to reset" event. There are two ways
this is triggered:
- From inside the guest — the guest kernel pokes the i8042 reset, as part of a
reboot. - From the operator —
PUT /actions {"action_type":"SendCtrlAltDel"}injects the equivalent, which the guest sees as Ctrl+Alt+Del and (if configured) reboots cleanly.
In Firecracker's model, a reboot is not a warm in-place restart of the guest — it ends the microVM. The process exits; the orchestrator decides whether to start a fresh one. This is consistent with the minimal, single-VM-per-process design: there is no firmware to re-run a boot sequence in place.
rg -n "SendCtrlAltDel|i8042|fn .*reset|exit" src/vmm/src/ | head -30
KVM_EXIT_SHUTDOWN and the vCPU loop
rg -n "KVM_EXIT_SHUTDOWN|VcpuExit::Shutdown|VcpuExit::Hlt|Exit|exit_evt|fn run" src/vmm/src/vstate/vcpu/
A guest power-off, a triple fault, or certain fatal guest conditions surface to the VMM as a KVM exit
reason in the vCPU's KVM_RUN loop:
| KVM exit | Meaning | Result |
|---|---|---|
KVM_EXIT_SHUTDOWN | guest shutdown / triple fault | the vCPU loop stops, signals the VMM to exit |
KVM_EXIT_HLT | guest executed HLT with no work | typically ends the vCPU run on x86 |
KVM_EXIT_FAIL_ENTRY / KVM_EXIT_INTERNAL_ERROR | KVM couldn't enter/continue the guest | fatal; VMM exits with diagnostics |
When a vCPU thread observes one of these, it doesn't exit silently — it propagates the intent to the
VMM thread (typically by writing an exit eventfd that the EventManager is
watching, and/or via the VcpuResponse channel). The VMM thread then begins orderly teardown. The
vCPU run loop and its exit dispatch are covered in
vcpu-run-loop-and-vm-exits.md; here the relevant part is only the
hand-off from a fatal exit to a process-wide shutdown.
flowchart TD
subgraph vCPU thread
Run["KVM_RUN"] --> Exit{exit_reason}
Exit -->|SHUTDOWN / triple fault| Stop["stop vCPU loop"]
end
Stop --> EvFd["write exit eventfd"]
subgraph VMM thread
EM["EventManager sees exit eventfd"] --> Tear["begin orderly teardown"]
end
EvFd --> EM
Reset["guest i8042 reset / SendCtrlAltDel"] --> EvFd2["reset event"] --> EM
Orderly teardown of the VMM
rg -n "fn stop|exit_code|FcExitCode|join|shutdown|drop|exit_evt|fn run_event_loop" src/vmm/src/
When the VMM thread decides to exit (fatal vCPU exit, reset, fatal API error, or a signal handler's request), the teardown is deliberate:
- Stop the vCPUs — send each vCPU thread a
VcpuEvent::Finish/exit (over theVcpuEventchannel) andjointhe threads, so no vCPU is still insideKVM_RUNwhile state is torn down. - Quiesce devices — drop device backends (close the TAP fd, the block file, the vsock socket).
- Set an exit code — Firecracker maps the cause to an
FcExitCode(find it withrg -n "FcExitCode|exit_code" src/vmm/src/), so an orchestrator can distinguish a clean guest shutdown from a crash. - Exit the process — the KVM fds, guest memory mmap, and epoll fd are released by process teardown.
The ordering matters: you must stop the vCPUs before releasing the guest memory they're running on, or you risk a use-after-free / fault in the very last moments. A fatal signal (SIGSEGV/SIGBUS/SIGSYS) short-circuits this — the handler logs and exits immediately, because the process is already in an unsafe state and the priority is to stop, not to clean up gracefully.
The jailer cleans up
rg -n "cleanup|remove|unlink|rmdir|cgroup.*remove|Drop|fn close" src/jailer/src/
After the firecracker process exits, the jailer-created artifacts need removing: the
chroot directory under /srv/jailer/<exec_file>/<id>/, the cgroup, and (if a fresh PID namespace was
used) the namespace teardown that follows the death of its PID 1. Some of this the kernel reclaims
automatically when the process and its namespaces die (an empty cgroup, a network namespace whose
last reference is gone); the chroot directory and any staged files are the orchestrator's
responsibility to remove. The division is the same as everywhere else in the security model: the
jailer owns the process-level sandbox lifecycle, the VMM owns its own internal teardown, and the
orchestrator owns the filesystem staging it created.
Warning: Leaked jail directories are a real operational issue at scale — thousands of
/srv/jailer/.../<id>/trees that nobody removed after a crash. Production tooling must reap them. This is one of the things the jailer-in-production lab makes you confront.
Reading exercise
# 1. The signal handlers and what each does.
rg -n "SIGSYS|SIGBUS|SIGSEGV|sigaction|register_signal_handlers" src/vmm/src/signal_handler.rs
# 2. How a SIGSYS reads the offending syscall.
rg -n "si_syscall|seccomp|bad syscall|num_faults" src/vmm/src/signal_handler.rs src/vmm/src/
# 3. The i8042 reset and SendCtrlAltDel.
rg -n "i8042|SendCtrlAltDel|reset|reboot" src/vmm/src/devices/ src/vmm/src/
# 4. The fatal KVM exits in the vCPU loop.
rg -n "KVM_EXIT_SHUTDOWN|VcpuExit::Shutdown|VcpuExit::Hlt|FcExitCode" src/vmm/src/vstate/vcpu/ src/vmm/src/
# 5. The teardown: stopping vCPUs before releasing memory.
rg -n "VcpuEvent|Finish|join|exit_evt|stop" src/vmm/src/
# 6. Reproduce a clean shutdown: boot a microVM, then from the guest run `reboot`,
# and watch the firecracker process exit. Inspect its exit code.
Answer:
- List the signals Firecracker handles and what each handler does. Which one is the seccomp boundary firing, and why is its appearance good news for the threat model?
- Why are signal handlers minimal, and why does Firecracker fail closed on a VMM fault rather than try to recover?
- Trace a guest
reboot: from the i8042 write to the process exit. Why is a reboot not an in-place warm restart? - What does
SendCtrlAltDeldo, and how does it relate to the guest-initiated reset path? - Name three fatal KVM exit reasons and how a vCPU thread hands a fatal exit off to the VMM thread.
- Why must the teardown stop the vCPUs before releasing guest memory? What does
FcExitCodelet an orchestrator distinguish?
Common bugs and symptoms
| Symptom | Root cause | Where to look |
|---|---|---|
VMM exits with SIGSYS / "bad syscall" | a denied syscall (real bug or missing filter rule) | seccomp-filtering.md; the SIGSYS handler |
VMM dies with SIGBUS touching guest memory | truncated/short memory file, bad mmap (often a snapshot memory file) | guest-memory mmap; snapshotting.md |
reboot in guest hangs instead of exiting | i8042 reset not caught; wrong reboot= mode in cmdline | partial i8042 handling; boot args |
SendCtrlAltDel does nothing | sent pre-boot, or guest not configured to act on it | action validity by VM state; guest config |
| vCPU thread fault during shutdown | guest memory released before vCPUs stopped | teardown ordering: join vCPUs first |
Leaked /srv/jailer/.../<id>/ after crash | nobody reaped the chroot dir | orchestrator cleanup; jailer artifacts |
Validation: prove you understand this
- Build the signals table from memory (signal → source → handler action) and explain the fail-closed philosophy.
- Explain why
SIGSYSis the seccomp boundary made visible and how its handler records the event. - Walk the guest-reboot path end to end and justify why it terminates the microVM rather than restarting it in place.
- List three fatal KVM exit reasons and describe the eventfd hand-off from the vCPU thread to the VMM thread.
- Order the orderly-teardown steps and explain the consequence of getting the vCPU-stop / memory- release order wrong.
- State which cleanup the kernel does automatically vs which the orchestrator must do for jailer artifacts.
Next: acpi-and-mptable.md — how the VMM describes CPU and device topology to the guest it just booted (and is now, perhaps, tearing down).