Mark seccomp as partially supported on amd64 and arm64 and document the accepted operation and flags.
Fixes#14627
Co-authored-by: Hanzo Dev <[email protected]>
gVisor returned `ENOSYS` for `copy_file_range(2)`, causing toolchains and
runtimes without fallback paths (such as Zig during linker output generation)
to fail inside sandboxes.
Implement `copy_file_range` as a generic buffered copy in the Sentry to
maintain backing filesystem independence and avoid widening the Sentry seccomp
filter. Argument validation and error handling mirror Linux kernel
`fs/read_write.c` semantics, including offset range checks, cross-device copy
support, and same-file overlap detection.
Add `copy_file_range_test` covering offset handling, short copies, overlap
detection, and Linux errno parity across runsc platforms.
PiperOrigin-RevId: 973465473
Seems that GNU tar extraction has been broken in gVisor since 75b03fd
(11/15/2025), which started using openat2(2) to address CVE-2025-45582.
tar uses openat2(2) for the RESOLVE_BENEATH flag which allows scoping
the path resolution underneath the specified dirfd.
This commit implements openat2(2) in gVisor, with support for all flags
supported by Linux: RESOLVE_BENEATH, RESOLVE_IN_ROOT, RESOLVE_CACHED,
RESOLVE_NO_MAGICLINKS, RESOLVE_NO_SYMLINKS, and RESOLVE_NO_XDEV.
This commit also introduces VirtualFilesystem.renameSeq, a global
sequence counter that along with mountSeq detects mount or rename
operations that race with scoped traversals.
A final "resolved-path-is-under-dirfd" check is performed after
the traversal. This is for two reasons: (1) in case of a bug in
any of the above logic, or (2) in case of an (extremely unlikely)
attack where mountSeq overflows by exactly 2^32 during a path
resolution. Unfortunately, due to the way path traversal in gVisor
is tightly coupled to the operation being performed by a VFS method,
using scoped traversals in an operation that *mutates* anything will
be difficult, since without major refactoring, the check must happen
after the side effect has taken place.
Currently, Linux only exposes scoped traversals through openat2(2),
so destroying the created fd is all we need to do if the final check
fails. (As a result of this limitation, most VFS functions have been
modified to reject scoped traversals "just-in-case".)
The RESOLVE_NO_XDEV implementation has a slight divergence from
Linux. It is a bit looser, permitting absolute symlinks in any case
where the root dentry's mount matches the mount the traversal is
locked to. Linux's behavior here is likely a bug, and since we can
safely be *more* permissive, there's not much reason to match it.
This commit also adds syscall tests and an strace formatter for
openat2(2).
Add an efaproxy driver plug-in for AWS Elastic Fabric Adapter (EFA) devices
bound to the host `efa` driver. EFA's driver-private uverbs ABI carries no
userspace buffer pointers on CQ/QP CREATE: queue memory is kernel-allocated
and handed back via mmap keys in the CREATE response, which the core already
forwards to the host FD verbatim. PrepareCreateDMA is therefore a no-op; the
plug-in exists to register the "efa" driver name and to model EFA's MR_QUERY
driver-namespace method (used by libfabric to fetch RDMA read/write
interconnect IDs).
Model the standard AH object's destroy method (UVERBS_OBJECT_AH): EFA (SRD)
creates address handles via the legacy write path and destroys them through
the modern object, which the core must allow.
Extend the virtual RDMA sysfs for libfabric/hwloc topology discovery:
- device/driver symlink and /sys/bus/pci/drivers/<driver> tree, which
libfabric's EFA provider resolves during device discovery.
- Mirror each PCI node's raw config space ("config" file). hwloc (used by
aws-ofi-nccl to build the NCCL topology) reads it to recover the PCI
bridge bus-number registers; without it hwloc cannot reconstruct the
bridge hierarchy and aws-ofi-nccl aborts its topology write.
- Serve the per-port lid_mask_count attribute alongside the other port
attributes libibverbs reads from sysfs; ib_core creates it on every
port.
The sysfs integration tests cover the new driver tree and config mirroring.
Advertise a >= 5.12 kernel release in RDMA-enabled sandboxes so aws-ofi-nccl
enables dmabuf-based GPU memory registration (ibv_reg_dmabuf_mr), which the
proxy handles via the existing REG_DMABUF_MR path. Sandboxes without RDMA
keep the current 4.19 release to limit the bump's blast radius; a later
change can make the newer release the default.
Co-authored-by: Alessio Ricci Toniolo <[email protected]>
GPU profilers (Nsight Systems/CUPTI) build their trace timeline from
CLOCK_MONOTONIC_RAW. gVisor serves CLOCK_MONOTONIC_RAW as an alias of
CLOCK_MONOTONIC, this will drift from CLOCK_MONOTONIC_RAW breaking profiling.
When nvproxy is enabled with `capProfiling`, this change exposes
CLOCK_MONOTONIC_RAW as a clock distinct from CLOCK_MONOTONIC that tracks the
host's CLOCK_MONOTONIC_RAW (absolute, unadjusted). CLOCK_MONOTONIC is
unchanged. When profiling is disabled, CLOCK_MONOTONIC_RAW continues to alias
CLOCK_MONOTONIC as before.
A tty inode was kept alive by a single ownership chain: the stdio file
description's reference on the import-time dentry. Everything else that used
the inode referred to it uncounted: a file description opened via /dev/tty
pins only the devtmpfs dentry, and the controlling-terminal association
(ThreadGroup.tty) is a bare pointer.
PiperOrigin-RevId: 962479941
gVisor ignored [`MAP_FIXED_NOREPLACE`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a4ff8e8620d3f4f50ac4b41e8067b7d395056843), treating the address as a hint. Reservations over occupied ranges silently landed elsewhere instead of failing with `EEXIST`. This breaks the NVIDIA driver's `PROT_NONE` placeholders over released `/dev/nvidia*` ranges during `cuda-checkpoint`: the ranges stay vacant in a memory snapshot, get reused on restore, and are then clobbered by the driver's `MAP_FIXED` remap → SIGSEGV.
Implement Linux semantics (`mm/mmap.c:do_mmap()`): exact placement or `EEXIST` on any overlap (including partial), never clobbering — even combined with `MAP_FIXED`. Unaligned addr → `EINVAL`. Existing `Fixed && !Unmap` callers keep `ENOMEM`. Also decode the flag in strace.
The goal is to differentiate O_DSYNC and O_SYNC, O_DSYNC behaves same
as O_SYNC today in gVisor.
assisted-by: agy
Fix#1897
PiperOrigin-RevId: 958610974
Adds initial scaffolding for eBPF in gVisor. It implements the bpf(2) syscall
with support for BPF_PROG_LOAD, BPF_PROG_QUERY, and BPF_PROG_ATTACH.
Necessary for the cgroups v2 flag to be enabled by default, as this fixes
Docker-in-gVisor with cgroups v2.
This change implements the PointListen trace point to capture details of the
listen syscall, including the file descriptor, backlog, socket family,
socket type, and bound address. It also updates the seccheck metadata for
amd64 and arm64, adds the Listen protobuf message, and includes test coverage.
PiperOrigin-RevId: 952916092
cgroup namespaces are non hierarchical and they are defined primarily by a
"root" cgroup node. The init cgroup namespaces's root node is the true root of
the (only) cgroup2fs tree. The root node of other cgroup namespaces is the
cgroup node of the task at the time of namespace creation.
Residing in a cgroup namespace affects tasks this way:
1) The views into /proc/[pid]/cgroup and the "root" field under
/proc/[pid]/mountinfo are virtualized: they are rendered relative to the
reading task's cgroupns.
2) mount()ing a cgroup2 fs furnishes only a subtree of the full cgroup2fs
tree, rooted at the "root" cgroup of the hosting cgroupns.
Fixes#11989
PiperOrigin-RevId: 948882249
This change includes the creation time (ctime) of the executable binary in the
seccheck ExecveInfo and MmapInfo.
The Timespec proto message is moved to common.proto for reuse.
PiperOrigin-RevId: 948385290
This change introduces a seccheck point for the mmap syscall on both amd64 and
arm64. A new proto message `Mmap` is defined to capture the syscall arguments,
including the file descriptor path when requested. The example seccheck server
is updated to handle the new message type, and a basic test case is added to
the trace tests.
PiperOrigin-RevId: 947914706
This change introduces a new seccheck point, "sentry/mmap", which is triggered
when a memory mapping is created. The seccheck event includes information about
the mapped file, such as its path, inode, mode, UID, and GID, as well as a
timestamp. It also indicates if the mapping corresponds to the initial
executable. This allows for monitoring of potential code injection or execution
from unexpected sources.
PiperOrigin-RevId: 944614086
Adds stub implementations for:
- sched_setattr(2)
- sched_getattr(2)
- ioprio_set(2)
- ioprio_get(2)
"Support" (with no effect) is included for SCHED_OTHER (referred to in Linux
as SCHED_NORMAL), SCHED_BATCH, and SCHED_IDLE. sched_getscheduler(2) and
sched_setscheduler(2) are updated to support all three as well.
For ioprio_set, the configured I/O priority has no effect.
PiperOrigin-RevId: 938270335
systemd uses this for some trivial options (ro, noswap).
I had originally wanted a proper implementation, but as far as I can
tell we have no logic to pass through reconfiguration data to underlying
filesystems, so properly supporting this will be a big change.
RemountAt(), used by MS_REMOUNT, claims to pass data to the underlying
fs, but it doesn't seem to do so. It only sets per-mount options.
For now, this behavior matches MS_REMOUNT.
Adds the infrastructure for a DynamicBytesFile to become pollable
(perhaps with select, poll, or epoll). Simply arrange for a
DynamicBytesPoller to be notified whenever the relevant file is updated
and implement PollableDynamicBytesSource on the DynamicBytesSource.
EPOLLPRI and EPOLLERR are triggered for these files, which seems to
match Linux's implementation for /proc/sys files.
The first two files to support this are hostname and domainname in
/proc/sys/kernel. This is used by systemd-journald to monitor for
hostname changes.
This frees implementations of getsockopt to no longer return EINVAL when
userspace specifies a buffer smaller than the output.
Some callers, such as resolved, pass a null buffer to probe the output
size before passing a real buffer to getsockopt. This allows those
callers to succeed.
This commit also removes EINVAL returns from netlink route sockets related to
the userspace-provided size to allow this pattern.
The first step in implementing the new file-descriptor based mount API in
gVisor. A "filesystem creation context" is created using fsopen(). The context
is then configured using fsconfig(), and the FSCONFIG_CMD_CREATE command is
used to create a filesystem. A "mount object" can be obtained using fsmount(),
which can be operated on directly using the *at() functions or mounted onto the
real directory tree using move_mount().
The current implementation has the following limitations:
- Linux passes configuration options to filesystems on-the-fly as fsopen() and
fsconfig() are called, allowing per-parameter error handling. gVisor
currently queues up all options and passes them to the filesystem all-at-once
when FSCONFIG_CMD_CREATE occurs. Lifting this will require refactoring the
option parsing for all existing filesystems.
- Only flag and string arguments to fsconfig() are supported.
- FSCONFIG_CMD_RECONFIGURE is not supported.
- move_mount(): flags MOVE_MOUNT_SET_GROUP and MOVE_MOUNT_BENEATH are not
supported.
- The filesystem context message retrieval interface is not supported.
In the interest of review size, there is no implementation yet for
mount_setattr(2), open_tree(2), or fspick(2).
PiperOrigin-RevId: 931245256
The arg to setdomainname isn't a string, it's a byte array with a
specified length.
This fix was previously applied to sethostname(2), but not
setdomainname(2).
The first step in implementing the new file-descriptor based mount API in
gVisor. A "filesystem creation context" is created using fsopen(). The context
is then configured using fsconfig(), and the FSCONFIG_CMD_CREATE command is
used to create a filesystem. A "mount object" can be obtained using fsmount(),
which can be operated on directly using the *at() functions or mounted onto the
real directory tree using move_mount().
For compatibility reasons, the new mount API is gated behind the runtime flag
`--new-mount-api`, which is disabled by default.
The current implementation has the following limitations:
- Linux passes configuration options to filesystems on-the-fly as fsopen() and
fsconfig() are called, allowing per-parameter error handling. gVisor
currently queues up all options and passes them to the filesystem all-at-once
when FSCONFIG_CMD_CREATE occurs. Lifting this will require refactoring the
option parsing for all existing filesystems.
- Only flag and string arguments to fsconfig() are supported.
- FSCONFIG_CMD_RECONFIGURE is not supported.
- move_mount(): flags MOVE_MOUNT_SET_GROUP and MOVE_MOUNT_BENEATH are not
supported.
- The filesystem context message retrieval interface is not supported.
In the interest of review size, there is no implementation yet for
mount_setattr(2), open_tree(2), or fspick(2).
PiperOrigin-RevId: 923640824
Thus far we were silently ignoring this flag.
While writing the syscall test for this change I realized that we were
using the async-signal-unsafe `FileDescriptor.reset()` (It's unsafe because
it eventually calls `getenv()`) and `exit()`
in various `fork()` contexts, so I fixed the affected syscall tests.
PiperOrigin-RevId: 925064507
Because we fetch from urandom under the assumption that the host urandom has
sufficient entropy, we can allow and silently ignore GRND_INSECURE for the
same reason we currently allow and silently ignore GRND_NONBLOCK.
The randomness will be more secure than what was requested, but this is fine.
PiperOrigin-RevId: 921880454
Without /proc/[pid]/setgroups the canonical rootless user-namespace setup
cannot run. The expected sequence starts by writing "deny" to
/proc/self/setgroups so that an unprivileged process can then write a
non-trivial gid_map, and that file did not exist in gVisor. Tools that
take this path, like buildah or podman, fail with ENOENT on
/proc/[pid]/setgroups before they can do any work.
I followed kernel/user_namespace.c. Each UserNamespace now carries a
setgroupsAllowed bit that is initialized true and inherited from the
parent at creation. Writing "deny" succeeds while gid_map is still empty
and clears the bit, while writing "allow" succeeds only if the bit is
still set, which preserves the one-way USERNS_SETGROUPS_ALLOWED
transition. The unprivileged branch of SetGIDMap now requires the bit to
be clear so that the behavior matches new_idmap_permitted, and
setgroups(2) is gated by the conjunction of the bit and a non-empty
gid_map, mirroring userns_may_setgroups.
Tests in test/syscalls/linux/proc_pid_uid_gid_map.cc cover the file's
read and write semantics, the one-way state transition, child-namespace
inheritance, the syscall gating before and after deny, and the gid_map
interaction. They pass on both runsc and native Linux.
Closes#1235 and removes the b/27454212 TODO that has sat in
user_namespace.go since the initial commit.
This is to support HWASan guests, which make these prctl calls on process
start.
gVisor (attempts to) use the tagged address ABI unconditionally, so these
prctl calls are no-ops. I will fix up the cases where it does not in follow
up changes.
PiperOrigin-RevId: 917415264
Refactor IOUringEnabled from being a global variable in pkg/sentry/kernel
to being a member of the kernel.Kernel struct. This improves encapsulation
and allows a consistent view between save/restore sessions.
PiperOrigin-RevId: 912220031
This patch introduces pidfd support roughly up to Linux 6.8. Linux selftests
at this version pass, and are now run as an //image_test target. Some features,
like PIDFD_THREAD, that occur later than 6.8 in Linux, are included.
It includes:
- These new syscalls:
- pidfd_open()
- pidfd_getfd()
- pidfd_send_signal()
- New aspects to these existing syscalls:
- waitid() in its support of P_PIDFD
- clone()/clone3() in their support of CLONE_PIDFD
- poll()/epoll()/select() in their support for pidfds
- setns() in its support for pidfds
- pid introspection via /proc/[pid]/fdinfo/[pidfd]
It excludes:
- pidfdfs: pidfds are backed by the anon inode right now
- ioctls to retrieve namespace fds using a pidfd
- process_madvise()
Updates #5839.
PiperOrigin-RevId: 908341658
This pool will be used to reuse buffers of size pipe.MaximumPipeSize in the
sendfile implementation, reducing allocation overhead.
This was inspired from the investigation of `bun install` slowness. The
repeated buffer allocation was causing a lot of GC churn and also spending a
lot of time in runtime.makeslice().
PiperOrigin-RevId: 905164509
## Summary
This patch adds capability and permission checks that the Linux kernel enforces but gVisor currently omits. Each fix was verified against native Linux behavior using `bazel test` on both native and `runsc_ptrace` platforms.
## Changes
### 1. `SO_BINDTODEVICE`: Add `CAP_NET_RAW` check
**File:** `pkg/sentry/socket/netstack/netstack.go`
**Linux reference:** `net/core/sock.c:sock_setsockopt()` checks `ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW)`
**Evidence this is unintended:** gVisor's own test suite asserts `"CAP_NET_RAW is required to use SO_BINDTODEVICE"` (`test/syscalls/linux/socket_bind_to_device.cc:52`), and `SO_RCVBUFFORCE` in the same file already correctly checks `CAP_NET_ADMIN`.
### 2. `mknod(S_IFBLK/S_IFCHR)`: Add `CAP_MKNOD` check
**File:** `pkg/sentry/syscalls/linux/sys_file.go`
**Linux reference:** `fs/namei.c:vfs_mknod()` checks `capable(CAP_MKNOD)` for block/char device creation
**Evidence this is unintended:** `CAP_MKNOD` is defined (`pkg/abi/linux/capability.go:56`), parsed from OCI specs (`runsc/specutils/specutils.go:491`), and has strace formatting — but is never checked anywhere. Zero `HasCapability` calls for it exist in the codebase.
### 3. `sched_setaffinity`: Add UID match / `CAP_SYS_NICE` check
**File:** `pkg/sentry/syscalls/linux/sys_thread.go`
**Linux reference:** `kernel/sched/core.c:check_same_owner()` requires EUID match or `CAP_SYS_NICE`
**Impact:** Without this check, any unprivileged process could modify another process's CPU affinity mask.
### 4. `setpriority`: Add UID match / `CAP_SYS_NICE` check
**File:** `pkg/sentry/syscalls/linux/sys_thread.go`
**Linux reference:** `kernel/sys.c:set_one_prio()` requires UID match or `CAP_SYS_NICE`
**Impact:** Without this check, any unprivileged process could change another process's scheduling priority.
## Testing
Tests added in `test/syscalls/linux/capability_checks.cc`, verified on both native Linux and gVisor:
```
bazel test //test/syscalls:capability_checks_test_native → 6/6 passed
bazel test //test/syscalls:capability_checks_test_runsc_ptrace → 4 passed, 2 skipped
```
The 2 skipped tests are the mknod positive cases (creating device nodes with `CAP_MKNOD`), which are skipped on gVisor because the sandbox does not permit device node creation regardless of capabilities.
| Test | What it verifies |
|------|-----------------|
| `SoBindToDeviceCapTest.RequiresCapNetRaw` | `EPERM` without `CAP_NET_RAW` |
| `MknodCapTest.CharDevRequiresCapMknod` | `EPERM` for `S_IFCHR` without `CAP_MKNOD` (native only) |
| `MknodCapTest.BlockDevRequiresCapMknod` | `EPERM` for `S_IFBLK` without `CAP_MKNOD` (native only) |
| `MknodCapTest.FifoDoesNotRequireCapMknod` | `S_IFIFO` succeeds without `CAP_MKNOD` |
| `SchedSetaffinityCapTest.OtherUidRequiresCapSysNice` | `EPERM` without UID match or `CAP_SYS_NICE` |
| `SetpriorityCapTest.OtherUidRequiresCapSysNice` | `EPERM` without UID match or `CAP_SYS_NICE` |
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/gvisor/pull/12872 from petrmarinec:fix/missing-capability-checks 70b4e0fa308ba182e7916112e90aed40cdea75f3
PiperOrigin-RevId: 903343461
Co-authored-by: Hanzo Dev <[email protected]>