As of Linux commit 5d39580f68e6, SO_ATTACH_FILTER requires
CAP_NET_ADMIN for TCP sockets.
Skip in tests if we don't have it, and update Netstack to require it
too.
Make existing mutex and atomic ownership explicit for networking state.
Annotate self-locking helpers and synchronous forwarding paths, keeping
queue references and callback lifetime requirements in their comments.
Document collection, callback and helper identity limits where the
checker cannot enforce the existing ownership contract.
Co-authored-by: Hanzo Dev <[email protected]>
Linux skips the mount-level write check when opening a character device, block device, FIFO, or socket, so write-opening /dev/null on a read-only bind mount succeeds.
Mark the remaining FileDescriptions backed by special-file inodes. Also skip overlay copy-up for writable opens of special files; Linux overlayfs applies the same special_file() exception before deciding whether copy-up is required.
Add regression coverage for character devices and FIFOs through read-only bind mounts.
- This change enables sockets to set and get the `SO_MARK` option, which marks
outgoing packets.
- It also implements Netfilter (iptables) support for matching
packet marks using the `mark` match extension.
- Added unit and E2E tests to verify mark match and capabilities.
PiperOrigin-RevId: 938123591
Linux's do_tcp_getsockopt() clamps the user-supplied buflen via
len = min_t(unsigned int, len, sizeof(int)) and copies that many
bytes of the integer value, so a sub-int (or zero-length) buffer
returns a truncated result rather than EINVAL. gVisor instead
rejects any outLen < 4 with EINVAL, which breaks real callers:
python-trio probes TCP_NODELAY with buflen=1 to detect its
availability and fails with OSError(EINVAL).
Route all 12 integer TCP options (TCP_NODELAY, TCP_CORK,
TCP_QUICKACK, TCP_MAXSEG, TCP_KEEPIDLE, TCP_KEEPINTVL,
TCP_KEEPCNT, TCP_USER_TIMEOUT, TCP_LINGER2, TCP_DEFER_ACCEPT,
TCP_SYNCNT, TCP_WINDOW_CLAMP) through a shared helper that
truncates the value to outLen bytes, matching Linux. outLen == 0
is a valid (zero-byte) read and must not be rejected.
Reference: net/ipv4/tcp.c:do_tcp_getsockopt()
Signed-off-by: Tan Yifeng <[email protected]>
Go map iteration randomness causes network interface enumeration
(getifaddrs, RTM_GETADDR, RTM_GETLINK, /proc/net/*, SIOCGIFCONF)
to return nondeterministic order, breaking applications like
asciidoctor that expect stable ordering across repeated calls.
Since gVisor allocates NIC IDs monotonically (like Linux ifindex),
sorting by NICID is equivalent to registration order. Add
InterfaceIDs() to inet.Stack so all consumer paths iterate in
deterministic ascending-ID order without ad-hoc sorting at each
call site.
Signed-off-by: Tan Yifeng <[email protected]>
## 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]>
When ctx is of type `kernel.taskAsyncContext`, calling
`kernel.TaskFromContext(ctx)` on it returns the original task (even though this
is an async goroutine). And then calling methods like Block() on it would panic
because it expects to be running on the task goroutine. Furthermore, the Task
could have exited so working on a dead task could cause other issues too.
Instead, use `kernel.taskAsyncContext` methods via the context.Context
interface. This change adds BlockWithTimeout() to the context interface and
implements it for context.NoTask. kernel.Task already implements this.
The current implementation of context.NoTask.BlockWithTimeoutOn was buggy:
- It returned true when the desired event occurred OR when the timeout expired.
However, context.Blocker interface docstring suggest that true should only
be returned when the desired event occurs, which kernel.Task implements
correctly.
- Instead of returning the remaining timeout, it returned the duration passed
since the timer started.
Fixed these two bugs and made the implementations of kernel.Task and
context.NoTask consistent with one another.
Also clarified docstrings for some Task.Block*() methods about the precondition
of "The caller must be running on the task goroutine.".
Reported-by: [email protected]
Reported-by: [email protected]
PiperOrigin-RevId: 853557307
linkat(AT_EMPTY_PATH) needs to check for CAP_DAC_READ_SEARCH in the userns in
which the fd was opened, not the userns of the task running linkat().
To get at that userns, vfs.FileDescription now saves a pointer to the creds
at the time of its creation: plumbing these creds in accounts for much of the
diff. This is similar to how `struct file` stores creds in `f_cred` in Linux.
Moreover, since the Linux patch "vfs: relax linkat() AT_EMPTY_PATH..." [1], the
need for this cap is excused if the creds of the calling task are literally
the same as the one stored in the fd.
[1] https://github.com/torvalds/linux/commit/42bd2af5950456d
PiperOrigin-RevId: 853374474
Of the 11 java runtime tests that were anticipating this socket option, 8 now
pass. Of the 3 that still fail, 1 now needs IPV6_MULTICAST_HOPS. The other 2
fail due to an unknown reason: they fail with runc too.
PiperOrigin-RevId: 813839126
gVisor has historically silently failed for unknown protocol options. In
the interest of not breaking existing users, we want to keep that behavior
while adding metrics to track how much this behavior is used in practice.
Eventually we'd like gVisor's behavior to be in line with Linux's.
PiperOrigin-RevId: 748459211
This set of changes adds:
- support for `xt_multiport_{,v1}` matchers for matching for
a range of ports and their inverse, i.e.,:
```
-m multiport [!] --[s|d]ports (PORT,...|PORT:PORT,...)
```
- support for `IP{,6}T_SO_GET_REVISION_MATCH` socket options,
which allows `iptables` to query for the highest supported
revision for a given matcher
This turns the uint64 metric constructor arguments into a struct, making it
more explicit as to what each part means. It also allows the creation of
non-cumulative uint64 (gauge) metrics, and adds methods to decrement or set
them.
PiperOrigin-RevId: 647134245
TCP_INFO option is supported in gVisor, but does not return all the fields
in struct TCP_INFO. The fields which were required for debugging are added.
The support for other fields in the struct can be added when required.
PiperOrigin-RevId: 632621246
SO_ACCEPTCONN returns true for TCP sockets which are in a listening state.
Added this socket option support for unix sockets.
PiperOrigin-RevId: 577008617
This change introduces the nsfs file system. Each new namespace allocates
a new nsfs inode.
Here are reasons why we need these inodes:
* each namespace has to have an unique id.
* proc/pid/ns/ contains one entry for each namespace. Bind mounting one of
the files in this directory to somewhere else in the filesystem keeps the
corresponding namespace alive even if all processes currently in
the namespace terminate.
* setns() allows the calling process to join an existing namespace specified
by a file descriptor.
PiperOrigin-RevId: 550694515
When the TCP forwarder ignores a connection due to having too many
in-flight connections, it's not easy to log a message or update a metric
for later debugging. Add a metric that will be incremented in this case
so that the user of the Forwarder can observe this.
Signed-off-by: Andrew Dunham <[email protected]>
The important change here is in tcpip/tcpip.go, where tcpip.Address is defined.
The rest is updating uses of tcpip.Address.
This is preparation for netip.Addr or []byte based addresses, which should save
us a bunch of allocations. Currently, we allocate every time we want to, say,
get a tcpip.Address from a header. This is because the header is a byte slice,
but Address is a string. Strings are immutable, so Go allocates and copies.
PiperOrigin-RevId: 532284732
This introduces a `metric.FieldValue` struct type that wraps a string.
All metric interfaces that deal with field values have been updated to use
pointers to this type instead of strings.
The intent of this change is to make it more obvious that field values must
be passed using references. Prior to this change, this was done using string
pointer comparisons. Now this must be done by using a pointer to the same
`metric.FieldValue` struct.
The struct type still externally exposes its string so that it can be referred
to in value function callbacks by "custom" metrics. (Though there are no
current uses of callback metrics with fields.)
PiperOrigin-RevId: 527030738
This catches up the interface to the `EmitUnimplementedEvent` method signature
on `kernel.Kernel`.
Also add build-time test to verify that `kernel.Kernel` implements this
interface, in order to catch such breakages at build time in the future.
PiperOrigin-RevId: 519000411