Add two-phase seccomp-BPF sandbox for the VMM process#86
Open
perbu wants to merge 2 commits into
Open
Conversation
Add a kernel-enforced syscall allowlist behind the guest-facing emulation layer. If a syscall handler is ever compromised, the attacker lands in a process that can no longer execve, ptrace, mount, or issue any syscall outside what the handlers need. Two stacking phases: a wide Init filter for guest initialization and warm-fork prepare, and a strict Runtime filter for request serving (KVM ioctls by type byte, translated-fd I/O, thread-only clone, clone3 -> ENOSYS fallback). Hand-rolled classic BPF against kernel headers only - no new dependencies. A log-only mode allows soak-testing allowlists via the kernel audit log before enforcing. The tables are derived from the handlers and validated against the unit test suite; embedders should soak with log_only and extend via SeccompOptions::extra_rules. See docs/seccomp.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in, host-side seccomp-BPF sandbox for the TinyKVM VMM process to constrain the VMM’s syscall surface area behind the guest-facing emulation layer, using a two-phase (Init → Runtime) tightening model plus a log-only soak mode.
Changes:
- Introduces
tinykvm::install_seccomp_filter()/seccomp_rules_for()API and a hand-rolled classic-BPF program generator. - Defines Init/Runtime allowlist tables (including KVM-ioctl arg filtering,
clone3 → ENOSYSfallback, TSYNC option). - Adds unit tests and documentation for installing/soaking the sandbox.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/seccomp.cpp | Adds unit tests validating Runtime enforcement, log-only mode, stacking intent, ioctl arg filtering, and extra rules. |
| tests/unit/CMakeLists.txt | Registers the new seccomp unit test on AMD64 and ARM64. |
| lib/tinykvm/linux/seccomp.hpp | Public API and data model for phases, rules, and options. |
| lib/tinykvm/linux/seccomp.cpp | Implements allowlists, BPF emission, and filter installation via seccomp() + PR_SET_NO_NEW_PRIVS. |
| lib/CMakeLists.txt | Links the new seccomp implementation into the library build. |
| docs/seccomp.md | Documents threat model, two-phase usage, thread scope implications, and log-only soak workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+27
to
+29
| #ifndef SYS_seccomp | ||
| #define SYS_seccomp 317 | ||
| #endif |
Comment on lines
+150
to
+152
| R{SYS_set_tid_address}, R{SYS_prlimit64}, | ||
| R{SYS_prctl}, R{SYS_getrlimit}, | ||
| R{SYS_getuid}, R{SYS_geteuid}, R{SYS_getgid}, R{SYS_getegid}, |
Comment on lines
+217
to
+228
| struct Check { uint32_t off; uint32_t mask; uint32_t value; }; | ||
| Check checks[8]; | ||
| unsigned num_checks = 0; | ||
| for (unsigned i = 0; i < rule.num_args; i++) { | ||
| const auto& arg = rule.args[i]; | ||
| const uint32_t mask_lo = arg.mask, mask_hi = arg.mask >> 32; | ||
| const uint32_t val_lo = arg.value, val_hi = arg.value >> 32; | ||
| if (mask_lo != 0) | ||
| checks[num_checks++] = {seccomp_data_arg_lo(arg.index), mask_lo, val_lo}; | ||
| if (mask_hi != 0) | ||
| checks[num_checks++] = {seccomp_data_arg_hi(arg.index), mask_hi, val_hi}; | ||
| } |
- allow seccomp(SECCOMP_SET_MODE_FILTER) in the Init phase so the Runtime filter can actually be installed on top of it; the stacking test passed spuriously because the SIGSYS it expected came from the blocked seccomp() call, not the banned syscall - make the SYS_seccomp fallback arch-specific (317 x86_64, 277 aarch64) - validate num_args and Arg::index before BPF emission so malformed extra_rules throw instead of reading out of bounds - REQUIRE fork() success in the test harness and strengthen the stacking test to prove the child survives the Runtime install Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Kernel-enforced syscall allowlist behind the guest-facing emulation layer: if a syscall handler is compromised, the VMM process can no longer execve, ptrace, mount, or issue anything outside what the handlers need.
Initfor guest initialization / warm-fork prepare, strictRuntimefor request serving (KVM ioctls by type byte, thread-only clone, clone3 → ENOSYS fallback).install_seccomp_filter().log_onlysoak mode reports allowlist gaps via the kernel audit log instead of killing.docs/seccomp.md.Notes for review:
log_onlybefore enforcing.🤖 Generated with Claude Code