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:

SignalSourceHandler behavior
SIGSYSseccomp SECCOMP_RET_TRAP on a denied syscalllog the offending syscall number + thread, increment seccomp.num_faults, exit
SIGBUSbad memory access — classically a guest-memory-file I/O error / truncated mmaplog, increment signals.sigbus, exit
SIGSEGVa segmentation fault in the VMMlog, increment signals.sigsegv, exit
SIGPIPEwriting 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:

  1. From inside the guest — the guest kernel pokes the i8042 reset, as part of a reboot.
  2. 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 exitMeaningResult
KVM_EXIT_SHUTDOWNguest shutdown / triple faultthe vCPU loop stops, signals the VMM to exit
KVM_EXIT_HLTguest executed HLT with no worktypically ends the vCPU run on x86
KVM_EXIT_FAIL_ENTRY / KVM_EXIT_INTERNAL_ERRORKVM couldn't enter/continue the guestfatal; 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:

  1. Stop the vCPUs — send each vCPU thread a VcpuEvent::Finish/exit (over the VcpuEvent channel) and join the threads, so no vCPU is still inside KVM_RUN while state is torn down.
  2. Quiesce devices — drop device backends (close the TAP fd, the block file, the vsock socket).
  3. Set an exit code — Firecracker maps the cause to an FcExitCode (find it with rg -n "FcExitCode|exit_code" src/vmm/src/), so an orchestrator can distinguish a clean guest shutdown from a crash.
  4. 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:

  1. 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?
  2. Why are signal handlers minimal, and why does Firecracker fail closed on a VMM fault rather than try to recover?
  3. Trace a guest reboot: from the i8042 write to the process exit. Why is a reboot not an in-place warm restart?
  4. What does SendCtrlAltDel do, and how does it relate to the guest-initiated reset path?
  5. Name three fatal KVM exit reasons and how a vCPU thread hands a fatal exit off to the VMM thread.
  6. Why must the teardown stop the vCPUs before releasing guest memory? What does FcExitCode let an orchestrator distinguish?

Common bugs and symptoms

SymptomRoot causeWhere 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 memorytruncated/short memory file, bad mmap (often a snapshot memory file)guest-memory mmap; snapshotting.md
reboot in guest hangs instead of exitingi8042 reset not caught; wrong reboot= mode in cmdlinepartial i8042 handling; boot args
SendCtrlAltDel does nothingsent pre-boot, or guest not configured to act on itaction validity by VM state; guest config
vCPU thread fault during shutdownguest memory released before vCPUs stoppedteardown ordering: join vCPUs first
Leaked /srv/jailer/.../<id>/ after crashnobody reaped the chroot dirorchestrator cleanup; jailer artifacts

Validation: prove you understand this

  1. Build the signals table from memory (signal → source → handler action) and explain the fail-closed philosophy.
  2. Explain why SIGSYS is the seccomp boundary made visible and how its handler records the event.
  3. Walk the guest-reboot path end to end and justify why it terminates the microVM rather than restarting it in place.
  4. List three fatal KVM exit reasons and describe the eventfd hand-off from the vCPU thread to the VMM thread.
  5. Order the orderly-teardown steps and explain the consequence of getting the vCPU-stop / memory- release order wrong.
  6. 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).