Skip to content

Latest commit

 

History

History
199 lines (171 loc) · 10.7 KB

AGENTS.md

File metadata and controls

199 lines (171 loc) · 10.7 KB

gVisor

What is gVisor?

gVisor is a userspace kernel used for sandboxing untrusted programs. It is Linux-like kernel implementation in Go.

NOTE: The project uses Bazel as a build system, so paths starting with // refer to the repository root, and //some/path:some_target refers to target some_target defined inside some/path/BUILD.

gVisor is made of multiple components:

  • The gVisor Sentry is the main component implementing a Linux-like kernel in Go. Its implementation is at //pkg/sentry.
  • Gofers are I/O proxies for the gVisor Sentry. There are multiple Gofers, but an implementation of one is at //runsc/fsgofer.
  • runsc is a Docker-compatible OCI-compliant container runtime that uses gVisor under the hood. It is useful to run Docker images in gVisor. Location: //runsc.

Important gVisor subpackages

  • The gVisor "platform" acts as a syscall interception mechanism for sandboxed applications, and which is responsible for redirecting control to the gVisor sentry for handling the system call. There are multiple platform implementations; the name "platform" refers to the abstraction. Location: //pkg/sentry/platform
  • The gVisor kernel package implements the bulk of the kernel logic. Location: //pkg/sentry/kernel
  • The Linux kernel ABI is redefined as a Go package so that we can use it in the gVisor kernel. Location: //pkg/abi.
  • The runsc CLI is the main entry-point into gVisor for most usage.
  • gVisor may also be used programmatically using a subprocess-like API via language bindings with the sandboxexec API; see //sandboxexec.

gVisor code conventions

See //.gemini/styleguide.md for a general style guide.

In the gVisor codebase, all Go packages use the same package name as the name of their parent directory. For example:

  • The package at //pkg/errors/linuxerr (which is the same as the BUILD target //pkg/errors/linuxerr:linuxerr) has Go import path gvisor.dev/gvisor/pkg/errors/linuxerr.
  • The package at //pkg/sentry/vfs has Go import path gvisor.dev/gvisor/pkg/sentry/vfs.
  • This implies that new libraries you may create must be under their own new subfolder. For example, if you wish to create a library called foo under an existing directory //pkg/bar, you should create the directory //pkg/bar/foo and then create a file //pkg/bar/foo/foo.go within it. The BUILD file at //pkg/bar/foo/BUILD would contain the go_library rule with name foo, and the package line within //pkg/bar/foo/foo.go would be package foo. The import statement for this package in other Go libraries would be gvisor.dev/gvisor/pkg/bar/foo, and the dependency target would be //pkg/bar/foo. Other Go source files within //pkg/bar/foo generally also belong to the same Go package, though it is also acceptable to use the foo_test package name for test files in the same //pkg/bar/foo directory, if having a separate package name is desirable.
  • gVisor's Bazel rules are wrappers over the usual ones, especially for go_library, go_binary, and go_test. They should be imported into BUILD files from //tools:defs.bzl, e.g. load("//tools:defs.bzl", "go_library") for the go_library wrapper. These wrapper rules create auto-generated Go source files that are added to the source code of each Go package, enabling code generation for Go code that gets injected into every package, such as automated serialization. You generally do not need to inspect these autogenerated Go source files, but be aware that they exist, and that what you see in the repository is not the entire picture that the Go compiler actually sees.
  • When writing tests that need to read external files as input, you should use the testutil.FindFile function defined in //pkg/test/testutil with import path gvisor.dev/gvisor/pkg/test/testutil. It takes in a path as argument, which is rooted from the // directory. For example, to access file //foo/bar/baz.txt, you would call testutil.FindFile("foo/bar/baz.txt"). Every external file that you expect to be able to find using testutil.FindFile must also be declared as a data dependency in the BUILD target of the go_test target you are editing. For example, to access file //foo/bar/baz.txt, the go_test BUILD rule would need to have data = ["//foo/bar:baz.txt"]. If you want an example, look at existing usages of this function to understand how it works, e.g. dumpfilter_test.go in //runsc/boot/filter/dumpfilter.
  • Despite being a bazel project, gVisor uses a Docker-based wrapper over bazel using make. The Makefile defines wrapper rules over bazel subcommands, which run these commands inside an automatically-managed Docker container. This container is useful to smooth over Bazel quirks and version differences across machines. For example, building a target is make build TARGETS=//path/to:target, and running a test is make test TARGETS=//path/to:some_test.
  • Comments must describe the code as it is for the future codebase readers, not for present-time PR reviewers. A comment that talks about a bug that the PR at hand fixes is counter-productive, because it leaves behind irrelevant historical context that future readers of the codebase should no longer have to worry about.
  • Function/struct/field comments should generally not refer to their callers (e.g. "Used by", "Useful for"). If this is not already clear from the function name or context, then the function name and/or context is the problem, and the comment is a band-aid.
  • Prose (such as comments, documentation, other markdown files) should mostly follow ASD-STE100 Simplified Technical English. Some liberties are expected (and welcome in some contexts, such as website/blog post contents), but technical content should be crisp, clear, and to-the-point.
  • gVisor is packaged as a fileset at //:release, and any consumers of this must treat this as an opaque, all-or-nothing set of files to install or set up. With narrow exceptions, they should not enumerate the set of files within.

General approach to resolving gVisor issues

Modifying gVisor kernel behavior

Use the gvisor-kernel-development skill.

Security first

All changes are reviewed for security. gVisor's design and architecture ensures that a Sentry compromise, however unlikely, is still tightly contained. Nonetheless, any changes to the Sentry are very security-sensitive and must be reviewed carefully to avoid introducing new escalation vectors. See SECURITY.md for various aspects of gVisor security.

Compatibility in practice

gVisor aims to match broad Linux-like behavior, but this is a moving target and some incompatibilities matter more than others in practice. If there exists no popular real-world application that runs into the compatibility problem that a PR is addressing, the PR review will be de-prioritized against those that do have popular real-world applications.

The pecking order, from most important to least, with compatibility code:

  • COMPATIBILITY:REGRESSION-GVISOR: Regressions in real-world application compatibility of a previously-supported application, due to past changes in gVisor
  • COMPATIBILITY:REGRESSION-APP: Regressions in real-world application compatibility of a previously-supported application, due to changes/updates by the application
  • COMPATIBILITY:UPCOMING-KERNEL: Incompatibilities not yet encountered, but known to later be encountered due to an upcoming change in the Linux kernel.
  • COMPATIBILITY:NEW-APP: Incompatibilities encountered in real-world applications that never worked in gVisor before, in their default configuration/use-case.
  • COMPATIBILITY:NEW-APP-CONFIG: Incompatibilities encountered in real-world applications that never worked in gVisor before, in a specific non-default configuration/use-case.
  • COMPATIBILITY:PREVENTIVE-APP: Syscall-test-only change that verifies a piece of working gVisor compatibility that isn't yet covered by syscall tests, which is known to be relied upon by a real-world application.
  • COMPATIBILITY:UPCOMING-APP: Incompatibilities not yet encountered, but known to later be encountered due to an upcoming change in an application that currently works in gVisor.
  • COMPATIBILITY:SYNTHETIC: Incompatibilities exercisable only via syscall tests, which no known real-world application encounters.

Performance sensitivity

As a kernel, gVisor performance matters almost as much as security. As such, any change that may impact performance is required to come with benchmark measurements to measure its benefit and to avoid regressions.

The following code is particularly performance-sensitive:

  • The system call hot path: entry and dispatch in pkg/sentry/kernel (task_run.go, task_syscall.go), the handlers in pkg/sentry/syscalls/linux, and the layers that copy data to and from application memory (pkg/usermem, pkg/safemem, pkg/safecopy, pkg/marshal).
  • Platform code (pkg/sentry/platform, pkg/ring0).
  • Serialization code (pkg/state).
  • Sandbox startup (runsc/boot, runsc/container, runsc/sandbox).
  • Netstack (pkg/tcpip).
  • Checkpoint/restore (pkg/state, pkg/sentry/state).
  • File I/O (pkg/sentry/vfs, pkg/sentry/fsimpl).
  • Memory management (pkg/sentry/mm, pkg/sentry/pgalloc).
  • Inter-process communication with the gofer (pkg/lisafs, runsc/fsgofer).
  • Go init() functions that run on every binary startup.

Load the gvisor-performance skill whenever any of these paths show up, or when a change has any possibility of performance implications. Test-only changes do not have performance implications.

Git / GitHub / PR Guidelines

Before sending PRs, check the following:

  • Compatibility fixes must come with a compatibility code (see "Compatibility in practice" section), along with an explanation for this code.
  • Syscall behavior must be verified to match Linux behavior by way of syscall tests, not Go tests.
  • Everything must build; use make build TARGETS=... building affected paths.
  • Relevant tests must pass; use make test TARGETS=... or make syscall-tests TARGETS=... as appropriate.
  • All PRs must pass make lint and make lint-cc.
  • When benchmarks are required, they must come with statistically-significant figures. See section on performance changes.
  • Website changes should come with before/after screenshots (use make website-server to start an HTTP server serving the site locally).
  • Test-only changes that improve stability/flakiness are very much welcome.