Repository navigation
Releases
v0.10.10022026110200
This release hardens the engines and the CI process. CI now runs the gVisor analyzers checklocks, checkconst, and checklinkname, and guest probes that check registers, signal frames, fork, exec, and code rewrites on every backend. The probes and an audit of process-wide state found and fixed per-sandbox memory accounting, trampoline slot races of the Windows systrap engine, a half-patched system call site of the Linux amd64 engine, TCP stalls after packet loss, and signal frame fields that differed from Linux.
Tests
- System call functions of the guest while their pages change: the guest program
codewritehas the new checkcode-text-toggle. One thread makes the pages ofsyscall.Syscall,syscall.RawSyscall, andsyscall.Getpidwritable and executable, writes one byte back to itself, and makes them executable only. Four more threads call those functions and verify the result. Linux leaves the code unchanged, so every call must return the process ID. - CPU state of a new process and a new thread: the guest program
forkprobecallsclonewith every general purpose register, the vector registers, and the FP control state loaded, and the child stores its registers before it runs other code. A forked process and a thread (CLONE_VMandCLONE_THREAD) must start with all of them, and the parent must keep them. Afterforkandexecve, the new program must start with the Linux defaults of the FP control state (amd64:MXCSR0x1f80and x87 control word0x37f;arm64:FPCR0).TestE2ESystrapForkStateruns it on every backend. - Signal frame contents: the guest program
sigframeprobeverifies the frame that the kernel builds for a signal handler. The handler copies theucontextand thesiginfoand then overwrites every register. The checks compare the copy with the registers of the interrupted thread: the general purpose registers, the program counter, the stack pointer, the signal stack and mask, and the vector and FP record (amd64: thexsavearea with its software bytes, magic words, andXSTATE_BV;arm64:fpsimd_context,esr_context, and the end marker). They cover a signal in user code, a store to a read-only page and to an unmapped page (si_code,si_addr, trap number, error code,CR2,fault_address, andESR), a system call that restarts, and a system call that returnsEINTR. A handler that edits the frame must change the general purpose, vector, and FP control registers aftersigreturn(X0 stays on arm64), and an edited program counter must skip a faulting instruction.TestE2ESystrapSignalFrameruns it on every backend. - Register state across events: the guest program
regprobefills every general purpose register, the vector registers, and the FP control registers with distinct values, then lets a system call, a page fault, or a signal happen. The signal arrives in user code, in a blocking call that returnsEINTR, in a blocking call that restarts, and in a fault that aSIGSEGVhandler resolves. The handler overwrites every register, so only a correctsigreturnrestores them.TestE2ESystrapRegisterStateruns it on every backend. The packageinternal/acceptprog/guestcpuholds the assembly forlinux/amd64(RAX to R15, XMM0 to XMM15 and YMM0 to YMM15 when the CPU has AVX, MXCSR, and the x87 control word) andlinux/arm64(X0 to X30, V0 to V31, FPSR, and FPCR). - Code rewrites from several threads: the guest program
codewritehas the new checkcode-churn-threads. Four threads rewrite a code page betweenmprotectcalls, run it, and replace it by a new mapping at the same address. Two more threads change the permissions and the mapping of the pages beside the code pages. A swapper replaces the page that a runner thread executes by a mapping of one of two memory files with the same code. The code callsgetpid, so an engine that patches system call instructions must patch and unpatch them under this load. Each run must return the result of the latest version.TestE2ESystrapCodeWritesruns the check on every backend. - Flaky
cpu.maxtest:TestE2ECPUMaxEnforcementfailed on a hostedwindows/arm64runner and on a loadedhvfhost. The Sentry bills the wall time of each run of a thread. The old checks read a busy Go program, and each wake-up of its runtime monitor billed the time that the host needed to run it. One busy loop read 1.06 to 1.17 CPU on the runner and 1.5 CPU on a loaded macOS host. The checks "max gives one CPU", "above demand never throttles", and the loop without system calls now run a static loop with one thread. It reads 1.000 CPU in 30 runs on the runner. The check "small quota" compared the loop counts of a loop at a tenth of a CPU and of a loop that never sleeps. On an Apple CPU, a native loop that runs 10 ms and sleeps 90 ms makes 0.02 of those iterations. A second version compared with a loop of the same duty cycle. On twowindows/amd64runs that loop made about half of the expected progress, and the check failed with shares of 1.9 and 1.833. The cause is probably a change in the speed of the host CPU between two windows. The check now runs a clock loop, which measures its running time with the clock of the guest. It asserts four values. The billed time is 0.07 to 0.13 CPU. At least 0.8 of the periods throttle. The time held back is 0.80 to 0.97 of the wall time. The running time is 0.75 to 1.2 of the billed time. The share is 0.86 to 0.98 onhvf, onwindows/amd64, and onwindows/arm64. The Sentry code did not change. - gVisor analyzers in CI: the job
build, vet, and auditnow runschecklocks,checkconst, andchecklinknameon every package for the five targets. A lock annotation violation, a changed assembly offset, or a mismatched//go:linknamefails CI.scripts/gvisorlint.shruns the same check locally, anddocs/gvisorlint.mdstates how to fix a finding. The first run reported annotation gaps and lock calls that tests skip. It led to the annotations+checklocksacquire,+checklocksrelease, and+checklocksforceon lock helpers, closures, andTryLockcalls, and it split the atomic policies ofStack.fibfrom the rules thatrouteMuguards.
Bug Fixes
- Close of a Windows named pipe listener of the shim:
go-winio0.6.2can blockwin32PipeListener.Closeforever. Its listener routine takes the close request while a pending connect ends with an error other than "closed". The routine does not record the close. It waits for a second request, andAcceptcan return a rawERROR_OPERATION_ABORTED.TestWindowsLogPipeRejectsCrossInstanceCollisionhung for 10 minutes onwindows/amd64in CI. Every pipe listener of the shim (task, stop, and log) now repeatsCloseuntil the routine exits.Acceptreturnsnet.ErrClosedafterClose.Closegives up after 5 seconds and returns an error. The log sink then does not wait for its accept goroutine. The stop server and the shutdown of the shim wait at most 5 seconds after the drain deadline for an accept loop that does not return.TestWindowsPipeListenerCloseRacesPendingConnectuses the real listener. - Half patch of a system call after a mapping swap on Linux
amd64: thesystrapengine of Linux patches amov $nr,%eax; syscallsite in three writes. It looked up the frame of the site again for each write. A guest thread could replace the mapping of the page between two writes, for example withmmapandMAP_FIXEDof a memory file. The first writes then went into the old frame and the last writes went into the new frame. The old frame kept the invalid byte0x06in place ofsyscall, and the guest died with SIGILL when it ran that frame again.code-churn-threadsfailed in 15 of 300 runs on a hostedlinux/amd64runner with debug logging. The three writes of a patch and of an unpatch now run in one call of the new methodMemoryManager.WithPinnedMappings. A thread that replaces the range waits for the call. The patch also compares the site with themovandsyscallthat trapped, and it skips the patch when another thread changed the code. - Lost reply of
ShutdownandShutdownSandbox: the shim closed the connection right after it wrote the reply. The ttRPC1.2.7client cancels its context when it reads end-of-file. If the goroutine of the call did not yet take the queued reply from its stream, the call returnedttrpc: closedfor a successful shutdown. The shim waited 20 ms to hide this, andTestShutdownReplyDrainsBeforeServerShutdownfailed on loadedlinux/arm64andwindows/arm64runners. A connection that wrote a response now stays open until the peer closes it or one second passes, and the drain waits for it. A client that keeps its connection open after a reply delays the exit of the shim by up to one second.TestShutdownReplySurvivesClientCallLagdelays the call goroutine of a real ttRPC client and failed in 7 of 20 runs before the fix. uc_stack.ss_flagsof a signal frame: the kernel setSS_ONSTACKwhenever the handler ran on the alternate signal stack. Linux sets it only when the interrupted code ran on that stack, so a program that checks the flag to detect a nested signal saw a wrong value. The flag now follows Linux in both directions: a handler withoutSA_ONSTACKthat interrupts code on the alternate stack also seesSS_ONSTACK. The guest programsigframeprobechecks it on every backend.- Threads that continue inside a patched system call on Windows
amd64: a thread could start at the return address of a patchedsyscall, or return from a slot, while another thread patched the page. The patch covered that address with ajmp, and the guest died with SIGILL insyscall.rawSyscallNoError. A guest that changed the protection of its own text caused this, and so did a signal handler that returned to an interrupted call. The engine now keeps every patch away from the addresses where threads continue. A site keeps...
v0.10.10012026110200
This release fixes a regression of v0.10.10002026110200: a signal corrupted the floating point registers of an arm64 guest on Linux and macOS hosts. It also stops the TaskSize search after one probe on a host with a hardware address space size.
Bug Fixes
- arm64 floating point state after a signal: in
v0.10.10002026110200a signal and itssigreturncorrupted the floating point registers of an arm64 guest. On a Linux host, FPSR and FPCR became0x46508001and0x210, so the divide-by-zero trap was on and1.0/0.0raisedSIGFPEon a CPU that traps. On macOS, the low 64 bits of V0 changed. The signal frame code read the floating point state as a wholefpsimd_contextrecord, but the platforms keep only its body, andhvfkept V0 to V31 first. Every arm64 platform now keeps FPSR, FPCR, and V0 to V31 in this order, and the signal frame code converts the fields one by one.rungvwithsystrapinside agvxguest on arm64 also gets the right floating point state in its stub.
v0.10.10002026110200
This release runs rungv with the systrap engine inside a gvx guest on Linux amd64 and arm64, and inside upstream runsc on Linux amd64. The guest kernel now gives the signal frames, the fault information, the mmap errors, and the ptrace behavior that an inner Sentry needs, as Linux does.
Features
rungvinside agvxguest and in upstreamrunsc:rungv run -platform=systrapruns inside agvxguest on Linux amd64 and arm64, and on macOS arm64 with-platform=hvf. It also runs inside Docker withrunsc-kvmandrunsc-systrapon Linux amd64. The arm64 case inrunscdoes not run, because the arm64 signal frame ofrunschas nofpsimd_contextrecord.docs/cli.mdsection 11 lists the flags, the results, and the limits. The guest kernel now meets what the stub of an inner Sentry needs:- An unaligned
MAP_FIXEDmmap above the task size fails withENOMEM, as in Linux. The size probe of the inner Sentry depends on it. - The arm64 signal frame holds the
fpsimd_contextrecord, theesr_contextrecord of a fault, and the end marker.sigreturnloads the floating point registers from the frame. Before this release the stub stopped with a wrong magic number, or looped for ever on the missing end marker. - The amd64 signal frame of a
SIGSEGVorSIGBUSfrom a page fault holds the trap number 14 and the page fault error code, as in Linux. PTRACE_SETREGS,PTRACE_SETREGSET, andPTRACE_POKEUSERtell the address space when they change theGSbase on amd64, asarch_prctldoes. Thesystrapengine then removes its syscall patches that useGS. Before this release a thread that a tracer attached to died in a patched syscall.- The host address space size is a search, not a list. It works below the hardware size and on a host that is a Sentry.
- The stub filter allows any
val3in the futex wake of the sysmsg code. - A stub unmaps its address space around a sealed mapping of the host kernel.
- The syscall thread of a stub on amd64 calls
arch_prctl(ARCH_SET_GS)once, so that an upstreamrunschost removes its syscall patches from the stub. - A fault that a host reports with the error code 0 (amd64) or the ESR value 0 (arm64) gets an inferred access: an instruction fetch when the fault address equals the instruction pointer, a read first, and a write for the second fault at the same instruction and address. Linux never reports 0 for a page fault, so no other host takes this path.
- An unaligned
TestE2ENestedRungvInRungv: runsrungvwithsystrapin arungvguest.TestE2EPtraceGSBaseKeepsPatchedThreadAlive: attaches to a running thread, sets itsGSbase with each ptrace request, and verifies that the thread keeps running its patched syscalls.
v0.9.10012026110200
This release fixes the memory limit of a sandbox in a process with several sandboxes and a race of the Windows systrap engine. CI now runs the same tests on windows/arm64 as on windows/amd64, with the race detector on windows/amd64.
Bug Fixes
- Memory limit of a sandbox:
Resources.MemoryBytesnow counts only the memory of its own sandbox. The limit counted the memory of every sandbox in the process, so an SDK user that ran several sandboxes in one process sawErrMemoryLimitin a sandbox that stayed far below its limit. - Registry tests on a host without a hypervisor:
TestE2EGateM2BusyboxIsStatic,TestE2EGateM2BusyboxAshInteractive,TestE2EGateM3ArchitectureSelection,TestE2EGateM4TortureUnderDefaultLockdown, andTestE2ERealLinuxBusyboxPythonNodeAcrossBackendsnamed thewhpxbackend and failed on thewindows-11-armrunner, which has no hypervisor. They now skip there whenGVX_ALLOW_NO_HYPERVISOR=1. - Windows
systrap: a guest no longer stops with "the stub trapped from unknown slot" when one thread makes a system call while another thread changes the memory of the same code page. The Sentry kept no record of a freed trampoline slot, so it could not decode a trap that a thread published just before the free. A freed slot now keeps its record until a new slot reuses its bytes. The thread of such a trap resumes through the Sentry and not through the freed slot, because a new slot can overwrite the slot bytes at any time.
Tests
- Weekly Windows scope: the Windows jobs of
e2e-images.ymlrun every registry gate, as the Linux jobs do: one step forgo test ./e2ewithoutTestE2EK3s*, and one step for the k3s tests.windows-latestruns each gate onsystrapandwhpx.TestE2EK3sMultiNodeMixedKernelsandTestE2EK3sConformance*stay Linux-only: the first needs a native k3s server, and the second runs onlinux/amd64only because of its cost.TestE2EK3sMultiNodeControlPlaneFailoverdoes not run onwindows-11-arm: it failed in every run there, because three k3s servers saturate the four cores and the first server loses its leader election when the second joins. It passes onwindows-latest.
v0.9.10002026110200
This release makes a gvx node a fuller Linux node for Kubernetes. NetworkPolicy works with kube-router and Calico through IP sets. The wireguard link, the connection tracker, the kernel log, /proc/diskstats, and policy routing follow Linux. Limits without a Linux knob are gone, and host-side values are tunable with --tune. It also adds --host-ca-certs and fixes several bugs of nested and overlay use.
Features
--host-ca-certs:rungv run --host-ca-certsandConfig.HostCACertsmake the guest trust the root certificates of the host, for a host behind a proxy that inspects TLS or a host with a private registry. The flag is off by default.rungvreads the roots before the host lockdown: fromSSL_CERT_FILE,SSL_CERT_DIR, or the system bundle on Linux; from the System, SystemRootCertificates, and login keychains with the SSL trust settings of the user, admin, and system domains on macOS; and from theROOTstore on Windows.SSL_CERT_FILEandSSL_CERT_DIRadd roots on every host OS.- At the start, the sandbox appends the roots that an image bundle lacks to each bundle that exists (
/etc/ssl/certs/ca-certificates.crt,/etc/ssl/cert.pem, and the RHEL and SUSE paths). An image without a bundle gets/etc/ssl/certs/ca-certificates.crt. The roots that no image bundle holds also becomegvx-host-*.crtfiles in/usr/local/share/ca-certificatesand/etc/pki/ca-trust/source/anchors, soupdate-ca-certificatesandupdate-ca-trustkeep them. The writes go to the layer above the image. A path below a mount stays as the mount shows it. - Only guest processes trust the roots. A container that
dockerdstarts does not.docs/docker-engine.mdsection 6 shows how to give a container or a build the root, anddocs/cli.mdsection 4.10 lists the rules. The sections 6.x ofdocs/docker-engine.mdmove to 7.x.
- At the start, the sandbox appends the roots that an image bundle lacks to each bundle that exists (
- Calico on k3s: Calico 3.28 in the VXLAN mode with the legacy iptables back end runs on a k3s node (
TestE2EK3sCalico).docs/kubernetes.mdhas the procedure and the limits. - Policy routing:
ip rule(RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE), several routing tables, route metrics, protocols, scopes, and the route types blackhole, unreachable, prohibit, and throw. The guest follows the lookup order of Linux, includingfwmark,suppress_prefixlength, andgoto.wg-quickwith AllowedIPs 0.0.0.0/0 works. SO_MARKin the route lookup: The mark of TCP, UDP, raw, and ICMP sockets and of the WireGuard link (fwmark) selects the route.- iptables for Calico and
wg-quick: The matchrpfilter, the goto flag of a rule (-g chain), a rule without a target, the targetCONNMARK(revisions 1 and 2), and the matchconnmark. A tracked connection has a mark thatconntrack -Land ctnetlink show. rp_filteron IPv4 input: The sysctlnet.ipv4.conf.*.rp_filtertakes effect on IPv4 input (strict and loose), withsrc_valid_mark.- Proxy ARP and conf sysctls: A link answers proxy ARP requests. New sysctl files are
conf/*/proxy_arp,src_valid_mark,conf/<link>/forwarding, andneigh/*/proxy_delay. - Queue counts of a new link:
RTM_NEWLINKacceptsIFLA_NUM_TX_QUEUESandIFLA_NUM_RX_QUEUES. net/ipv4/conf/{all,default,<link>}/rp_filter: Calico readsconf/default/rp_filterat start. Before this release the file did not exist, and Calico failed withcould not determine default rp_filter setting. The files store any integer and start at 0. A link followsconf/defaultuntil a write to its file, and a write toconf/allreaches no link, as in Linux. A new network namespace copiesallanddefaultfrom the root namespace, and later writes stay in each namespace.netstack.Stack.EffectiveRPFilterreturns the larger ofalland the link value.ioctl(SIOCETHTOOL)for offload features: Calico Felix, theethtooltool,github.com/safchain/ethtool, andvishvananda/ethtoolread and change the offload features of a link. Before this release the ioctl returnedEOPNOTSUPP, and Felix loggedfailed to disable checksum offload: operation not supportedforvxlan.calico.- The commands are
ETHTOOL_GFEATURES,ETHTOOL_SFEATURES,ETHTOOL_GSSET_INFOandETHTOOL_GSTRINGSforETH_SS_FEATURES, the get and set commands for checksum, scatter-gather, TSO, GSO, and GRO,ETHTOOL_GFLAGS,ETHTOOL_SFLAGS,ETHTOOL_GDRVINFO,ETHTOOL_GLINK,ETHTOOL_GSET,ETHTOOL_GLINKSETTINGS,ETHTOOL_GPERMADDR, andETHTOOL_GET_TS_INFO. Other commands returnEOPNOTSUPP, and a set command withoutCAP_NET_ADMINreturnsEPERM. - Each link starts with the features of its Linux driver (loopback, veth, bridge, vxlan, wireguard, and
eth0). A set then a get returns the new value. The dependency rules of Linux apply: switchingtx-checksum-ip-genericoff also turns scatter-gather, TSO, and GSO off, and switching it on brings them back. A feature that the driver fixes returnsETHTOOL_F_UNSUPPORTEDorEOPNOTSUPP. The state follows a link into another network namespace and ends with the link. The features do not change how the guest sends packets.
- The commands are
/sys/fs/bpfand the file system typebpf: the directory/sys/fs/bpfexists in every sysfs mount, as in Linux, andmount -t bpf bpffs /sys/fs/bpfworks. Before this releaseruncfailed withmkdirat .../sys/fs/bpf: operation not permitted, a hostPath of typeDirectoryfor/sys/fs/bpffailed, and the Calico init containermount-bpffscould not mount./proc/filesystemslistsnodev bpf, andstatfsreturnsBPF_FS_MAGIC. The file system holds directories only, because the guest has no BPF objects to pin.mkdirof an existing name in sysfs now returnsEEXIST, notEPERM.- IP sets, the match
set, and the targetSET: theipsettool,kube-router, and Calico create and use IP sets in the guest, andk3snow enforces a NetworkPolicy. Before this releasek3sloggedSkipping network policy controller start, ipset save failed, and a NetworkPolicy had no effect.- The subsystem
NFNL_SUBSYS_IPSETofNETLINK_NETFILTERserves the protocol versions 6 and 7:PROTOCOL,CREATE,DESTROY,FLUSH,RENAME,SWAP,LIST,SAVE,ADD,DEL,TEST,HEADER,TYPE,GET_BYNAME, andGET_BYINDEX.ipset restore, batches of elements, and-existwork. An error message carries the request, as in Linux, so the tool decodes it. A listing has no size limit, because the socket sends one message for each read. - The set types are
hash:ip,hash:net,hash:ip,port,hash:net,port,hash:ip,port,ip,hash:ip,port,net,hash:net,net,bitmap:port, andlist:set, with the revisions of Linux 7.0. The options arefamily inetandinet6,timeout,counters,comment,skbinfo,forceadd,maxelem,hashsize,bucketsize,netmask,bitmask, andnomatchfor the net types. A net set tests the longest prefix first, so anomatchelement of a longer prefix wins over a covering net. An element with a timeout expires. The sets belong to the network namespace. - Linux limits follow the Linux knobs. The default
maxelemis 65536. The file/sys/module/ip_set/parameters/max_sets(default 256) sets the first size of the table of sets of each network namespace that starts after a write, and the table grows by 64 sets, as in Linux. -m set --match-set NAME src,dst(revisions 1 to 4, with!,--return-nomatch, and the counter conditions of the revisions 3 and 4) and-j SETwith--add-set,--del-set,--exist,--timeout, and--map-set(revisions 1 to 3) work for IPv4 and IPv6.iptables-saveprints the rules back unchanged. A rule keeps a reference to its set, soipset destroyfails withSet cannot be destroyed: it is in use by a kernel componentuntil the rule ends.iptablesfinds a set by name throughgetsockopt(SO_IP_SET).ipset swapexchanges the data behind two names. A rule that names a set sees the new data at once, as in Linux.
- The subsystem
- Block statistics from real I/O in
/proc/diskstats,/proc/partitions, and/sys/block: the root image and eachext4litevolume are disks of the guest, and their counters count the I/O of the Sentry on the host file. Before this release/proc/diskstatshad one disk,vda, that counted the read and write system calls of the processes. A socket, a pipe, or a terminal changed it, a volume had no disk, and cAdvisor in thekubeletcould not map the file system of a volume to a disk.- The root image is
vda(254:0) and it is read-only. Each mounted volume takes the lowest free name:vdb(254:16),vdc(254:32), and so on. A guest without an image has novda, and its first volume isvda. The disk ends when the mount ends. - The files of a volume report the device number of the disk as
st_dev./proc/<pid>/mountinfoand/proc/<pid>/mountsshow/dev/vdbas the mount source, and the directories of the root overlay report 254:0./proc/diskstats,/proc/partitions,/sys/block/<disk>/{dev,size,stat,ro},/sys/class/block/<disk>, and/sys/dev/block/<major>:<minor>name the same numbers. - The counters have the 17 fields of Linux 5.5 and later, in sectors of 512 bytes. One
ReadAtorWriteAtcall of the volume engine on the image file is one request. OneSynccall is a flush, and aTruncatecall that shrinks the file is a discard. The times come from the monotonic clock, with the in-flight, I/O, and weighted times of Linux. The merged counts stay 0, because the Sentry has no request queue. - The root disk counts the file data that the EROFS driver takes from the image layers. A page of 4 KiB counts once, at its first read or its first guest mapping, as a page cache fill does on Linux, so a second read or a second
execof a file counts nothing. The writes of the guest to its root go to memory, so the sectors written of the root disk stay 0. The metadata of an image is not counted. /proc/<pid>/iois unchanged ...
- The root image is
v0.8.10012026110200
Bug Fixes
- New connections from a pod to an address of the node timed out at random: on a node that forwards packets, a pod that connected to an address of
eth0(the API server at10.0.2.15:6443, or the cluster IP10.43.0.1:443thatkube-proxyrewrites to it) got no answer to its SYN for up to 127 seconds. Established connections and connections from the host were not affected. In ak3snode about 1 run in 4 had this fault, and 1 to 4 tests of the[sig-network] Servicesconformance group failed, with adial tcp 10.43.0.1:443: i/o timeoutor aservice is not reachablemessage.- The stack gave a packet for a local address of another NIC to the first NIC that owned the address. Several NICs own such an address, because the loopback NIC holds a temporary copy of every local address that a local route uses, and the stack lists the NICs in random order. The TCP listener then built the SYN-ACK route for that NIC. The route table of a NIC that owns the source address chose the default route through
eth0, and the SYN-ACK left the sandbox instead of the bridge. The share of lost SYN-ACKs depended on the number of NICs and on the local connections of the node, so a run had a high or a low rate for its whole time. - The stack now delivers such a packet on the NIC where it arrived, as Linux does. It still marks a packet to a broadcast address of the owner as a broadcast, and it still drops a packet from a broadcast source address of the owner.
- A reply with a local address of another NIC now uses the route table, as in Linux. A route through the NIC of the packet wins. Without such a route, the first matching route decides, so a host with two NICs still answers through the other NIC.
- The stack gave a packet for a local address of another NIC to the first NIC that owned the address. Several NICs own such an address, because the loopback NIC holds a temporary copy of every local address that a local route uses, and the stack lists the NICs in random order. The TCP listener then built the SYN-ACK route for that NIC. The route table of a NIC that owns the source address chose the default route through
Known limitations
- IPv6 pods: IPv6 has the same random choice of the NIC for a packet to a local address of another NIC, so a SYN-ACK can leave through the wrong NIC.
k3suses IPv4 by default, and the C5 tests use IPv4 only.docs/kubernetes.md, section 4.3, lists the limit.
v0.8.10002026110200
This release lets an unmodified k3s node run in a sandbox (GVXIP-005). It adds the Linux kernel interfaces that the kubelet, containerd, runc, and k3s read and write (phase P1). It adds the service data path of kube-proxy in the iptables mode (phase P2), the wireguard and vxlan links between nodes (phase P3), and the enforcement of cpu.max (phase P4). It adds no flag and no SDK field. docs/kubernetes.md shows how to start a single node and a cluster with two nodes.
Features
memory.statat the root cgroup:/sys/fs/cgroup/memory.statexists and reports all memory of the sandbox, as in Linux. The cAdvisor of thekubeletreads it for the memory of the node, sokubectl top nodeshows memory. The root still has nomemory.currentand nomemory.max.cpu.statusage of a removed cgroup stays in its parent:usage_usecof a parent counts its live children and the children that it removed. It fell to 0 when a child was removed. Pod cgroups of thekubeletlost the CPU time of finished containers, sokubectl top podshowed 0./dev/kmsg: the character device 1:11 opens for read and write. A non-blocking read returnsEAGAIN, and a blocking read waits.lseekwith offset 0 returns 0 forSEEK_SET,SEEK_DATA, andSEEK_END. A write goes to the Sentry log and returns its length. Thekubeletno longer exits withopen /dev/kmsg: no such file or directory. The Sentry has no kernel ring buffer, so a reader never gets a record.- Sysctls of the
kubeletand the container runtime:vm/overcommit_memoryaccepts 0, 1, and 2 and returnsEINVALfor other values.vm/panic_on_oom,kernel/panic,kernel/panic_on_oops,kernel/keys/root_maxkeys, andkernel/keys/root_maxbytesstore a value in the range of Linux.net/netfilter/nf_conntrack_max,nf_conntrack_tcp_timeout_established,nf_conntrack_tcp_timeout_close_wait, and/sys/module/nf_conntrack/parameters/hashsizestore their values. The files change no behavior of the Sentry. ip_unprivileged_port_start: each network namespace has the sysctl. The root namespace of the sandbox is the namespace of the container. It starts at 0, as in a Docker container, and no bind changes there. A namespace that the guest creates starts at 1024, as on Linux. Abindof a TCP or UDP socket of IPv4 or IPv6 to a lower port fails withEACCESunless the caller hasCAP_NET_BIND_SERVICE.runcno longer fails every pod sandbox withopen /proc/sys/net/ipv4/ip_unprivileged_port_start: no such file or directory.- Safe sysctls of Kubernetes:
net/ipv4/ip_local_port_range,tcp_syncookies,ping_group_range,ip_local_reserved_ports,tcp_keepalive_time,tcp_keepalive_intvl,tcp_keepalive_probes,tcp_fin_timeout, andkernel/shm_rmid_forcedare writable. The files take the range of Linux, soping_group_rangegoes up to 4,294,967,294 and the keepalive and FIN-WAIT-2 times accept 0. The network values belong to the network namespace of the process, so a write in a pod namespace leaves the value of the node. The port range, the keepalive values of new sockets, the FIN-WAIT-2 time, andtcp_syncookies=2reach the network stack. The stack gets each time as at least one second and the probe count from 1 to 127.ping_group_rangeandip_local_reserved_portsonly store their values, andtcp_syncookies=0still sends cookies when the accept queue is full. - Capacity of the node: with
-m,MemTotalin/proc/meminfoandtotalramofsysinfo(2)equal the memory limit when it is below the host RAM. The value belongs to each sandbox, so several sandboxes in one process, such as in the SDK, each see their own limit. A tmpfs mount without a size andsize=N%still follow the host RAM. With--cpus, the online CPUs,sched_getaffinity,/sys/devices/system/cpu/online, andnprocalready showed the whole CPUs of the quota. Without the options nothing changes. - Generic netlink:
socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC)works. The controller familynlctrlanswersCTRL_CMD_GETFAMILYby name, by ID, and as a dump. It sends the attributes thatlibnl,libmnl, andgithub.com/mdlayher/genetlinkread. An unknown family returnsENOENT, and an unknown command returnsEOPNOTSUPP. A dump is at most 192 KiB. A larger dump fails withENOBUFSand never ends with a partial list. wireguardlink (GVXIP-005, phase P3):ip link add wg0 type wireguard,wg set,wg show, andwg-quickwork. The link has the MTU 1420. The familywireguardofinclude/uapi/linux/wireguard.hsets and reads the private key, the listen port (0 picks a free port), the mark, and the peers. A peer has a key, a preshared key, an endpoint, a keepalive, and allowed IP addresses. A read also returns the last handshake time and the byte counters.- Both commands need
CAP_NET_ADMIN, and the answer holds the private key and the preshared keys, as on Linux. A request that sets the listen port or the mark needsCAP_NET_ADMINin the user namespace of the namespace that created the link. - A set request applies the mark, the port, the removal of every peer, the private key, and the peers in the order of Linux. A listen port that another socket uses fails with
EADDRINUSE, and the device keeps its port and its peers. A peer protocol version other than 1 fails withEPFNOSUPPORT. - A sandbox has at most 256
wireguardlinks, and a device has at most 4,096 peers. Both limits fail withENOMEM. - The device is
wireguard-go. It sends and receives UDP through the network stack of the namespace where the link was created. The encrypted packets leave through the routes of the guest. A published UDP port (-p 51820:51820/udp) or a keepalive admits the peers. ip link deland the end of the namespace close the device. A netstack link has no down state, so the device owns its UDP port at creation. The mark is stored and reported, but it marks no socket.
- Both commands need
vxlanlink (GVXIP-005, phase P3):ip link add vxlan1 type vxlan id 42 dstport 4789 local 10.0.0.1 dev eth0 nolearningworks, and so does the call ofgithub.com/vishvananda/netlinkthatflannelmakes. The link is an Ethernet device. Its MTU is the MTU of the underlay link minus 50 (70 for IPv6). That is 1450 oneth0. The MTU stays between 68 and 65,485.ip -d link showandRTM_GETLINKreport the kind and the attributes inIFLA_INFO_DATA, soflannelcan compare its configuration with the link.- A request reads the VNI, the destination port (8472 when absent),
localandgroup(IPv4 or IPv6), and the underlay link. It also reads learning (on when absent), TTL, TOS, DF, ageing, limit, the source port range, GBP, and the UDP checksum flags. The link reports each of them. - A VNI above 16,777,215 fails with
ERANGE. A second link with the same VNI, address family, and port fails withEEXIST. Links with the same family and port and different VNIs share one UDP endpoint on the wildcard address. - A multicast group,
external(flow-based), and GPE fail withEOPNOTSUPP. So does a request that changes an existing link withIFLA_INFO_DATA, such asip link set vxlan1 type vxlan. The same holds for awireguardlink. - The link sends each frame as a VXLAN datagram (RFC 7348) through the routes of the network stack that created it. The outer packets leave through
eth0and its NAT, or throughwg0. An outer route that leaves through avxlanlink drops the packet. - The link receives the datagrams on a UDP endpoint of that stack. It gives each frame to the link with the VNI of the datagram. The outer source port comes from a hash of the flow, inside the source port range, as in Linux. The UDP checksum of an IPv4 outer packet is off unless
udpcsumis set. - A frame goes to the remotes of the FDB entry for its destination address, or to the all-zeros entry. With learning on, the link learns the source address of each received frame. A learned entry ends after the ageing time, and a frame that would move a static entry is dropped.
- One FDB holds at most 65,536 entries, or the link limit when that is smaller. A new entry beyond the limit fails with
ENOSPC. An entry has at most 1,024 remotes. - The link binds its port at creation, because a netstack link has no down state. The link stores and reports
ttl inherit,df inherit,label,udp6zerocsumrx,remcsumtx,remcsumrx,remcsum_nopartial,proxy,l2miss,l3miss, andrscwithout enforcing them. It does not send to a multicast group.
- A request reads the VNI, the destination port (8472 when absent),
- Neighbor and FDB netlink (GVXIP-005, phase P3):
RTM_NEWNEIGH,RTM_DELNEIGH, andRTM_GETNEIGHwork for IPv4 and IPv6 and for the familyAF_BRIDGE.ip neigh replace 10.244.2.0 lladdr 02:42:ac:11:00:02 dev flannel.1 nud permanentandbridge fdb append 00:00:00:00:00:00 dst 10.0.0.2 dev vxlan1work, and so doNeighSet,NeighAppend,NeighDel, andNeighListofgithub.com/vishvananda/netlink.- A permanent or noarp entry becomes a static neighbor of the stack. A dump lists the entries of every link, with the states of the neighbor cache as
NUD_*values. A dynamic state in a request fails withEOPNOTSUPP, and so does a link without address resolution, such asloandwg0.NLM_F_EXCL,NLM_F_REPLACE, andNLM_F_CREATEfollow Linux. - A link has at most 65,536 static neighbors for each IP family. One more fails with
ENOBUFS, and a change of an existing entry still works. - An FDB request needs a
vxlanlink and the stateNUD_PERMANENTorNUD_REACHABLE. It readsNDA_LLADDR,NDA_DST,NDA_PORT,NDA_VNI, andNDA_IFINDEX.NLM_F_APPENDadds a remote to the all-zeros address and to a multicast address, andNLM_F_REPLACEchanges the remote of a unicast address, asvxlan_fdb_updatedoes. - A delete with a remote removes that remote, and the las...
- A permanent or noarp entry becomes a static neighbor of the stack. A dump lists the entries of every link, with the states of the neighbor cache as
v0.7.10002026110200
This release adds Docker and ext4lite features and fixes HotSpot 21, membarrier, and brk on the Windows systrap engine. It also changes the format of .gvx_meta, so an older release cannot open a workspace that this release wrote. Read the upgrade note under Behavior Changes before the upgrade.
Features
dockerd --userland-proxy=false:docker:dindnow starts with the option. The guest has/proc/sys/net/ipv4/conf/<link>/route_localnet, which changes the martian filter of that link only, and/proc/sys/net/ipv6/conf/<link>/accept_rafor each link,/proc/sys/net/bridge/bridge-nf-call-*, and the hairpin mode of a bridge port. A connection to127.0.0.1and a published port reaches the container in the guest, and the host port still works. The bridge does not pass frames throughiptables, so--icc=falsedoes not separate containers of one bridge.docker stats: the command shows the memory, the CPU time, and the network counters of a container. The guest now reportsIFLA_STATS64for each link, andmemory.statexists in each memory cgroup. A container that wrote 64 MiB shows 64 MiB onhvfand 101 MiB on Linux systrap, becausememory.currentcounts page cache and mapped pages.BLOCK I/Ostays at0B.iptables-legacy-save: the command prints the rules of the tablesfilter,nat,mangle, andraw. The guest has/proc/net/ip_tables_namesand/proc/net/ip6_tables_names.memory.maxof nested cgroups:docker run -m 32mends the process with the highest score when the container passes the limit. The exit code is 137,docker inspectshowsOOMKilled: true, andmemory.eventscountsoomandoom_kill. The Sentry compares anonymous memory plus tmpfs data with the limit, never ends the init process or a process withoom_score_adjof -1000, and checks every 5 ms or more while a container uses half of its limit, otherwise every 25 ms.memory.oom.groupends every process. A container can pass the limit by what it allocates between two checks. The enforcer stays off under the Windowssystrapengine.memory.high,cpu.max, andcpu.weightstay unenforced. Seedocs/docker-engine.md, section 6.4.O_TMPFILEonext4litevolumes:openwithO_TMPFILEcreates an unnamed file instead of failing withEOPNOTSUPP. The volume frees the file at the last close, and a crash cannot leak it, because the orphan list of ext4 holds the inode.O_TMPFILEwithoutO_EXCLfiles acceptlinkatwithAT_EMPTY_PATHand through/proc/self/fd. The link moves the inode out of the orphan list in one transaction. A file with no link fromO_TMPFILE|O_EXCLor fromunlinkstill refuseslinkatwithENOENT.fallocatemodes onext4litevolumes:FALLOC_FL_ZERO_RANGE(with and withoutFALLOC_FL_KEEP_SIZE),FALLOC_FL_COLLAPSE_RANGE, andFALLOC_FL_INSERT_RANGEwork. The checks and the error codes follow Linux: block alignment givesEINVAL, a collapse that reaches the end of the file givesEINVAL, and a size above the limit givesEFBIG. Each call is one journal transaction, ande2fsck -fnaccepts the image after it.RWF_*flags onext4litevolumes:preadv2andpwritev2acceptRWF_NOWAIT,RWF_APPEND, andRWF_NOAPPEND, as well asRWF_HIPRI,RWF_DSYNC, andRWF_SYNCfor both calls.RWF_APPENDwithRWF_NOAPPENDgivesEINVAL, and an unknown flag givesEOPNOTSUPP.RWF_NOWAITnever returnsEAGAIN, because the volume has no page cache between a call and the engine.- Hole punching on Windows
hostfs:fallocatewithFALLOC_FL_PUNCH_HOLEmarks the file sparse and callsFSCTL_SET_ZERO_DATA, so NTFS frees the clusters of the range. Before, the call wrote zeros and freed nothing. A volume without sparse files, such as FAT or exFAT, and a file whose NTFS attribute list is full keep the zero-fill. The guestst_blocksstill shows the logical size on Windows.
Bug Fixes
- HotSpot 21 on the Windows
arm64systrapengine:javaexited withCould not allocate compressed class space: 1073741824 bytes. HotSpot onarm64asks for the class space at 38 fixed addresses between 4 GiB and 1 TiB, and the guest had no address there. Guests now get all 38 addresses onwindows/arm64andwindows/amd64. A middle window from0x7fff0000to 66 GiB holds 9 of them, and a window of 2 GiB holds each of the other 29. The high window now starts at 4 TiB instead of 16 TiB. The memory manager supports several address holes, so the platform reports the ranges between the windows. The kernel places the initial stack and the PEB of every stub between 4 GiB and 1 TiB, and it refuses to move the PEB. About 12% of the new stubs therefore hold part of a window. The engine ends such a stub and starts another one, up to 12 times. A platform that drops a window writes a warning to the log. AddressSanitizer and Wine 64-bit still needwhpx.TestE2EGateJVMruns Temurin 21 on every backend: C2, eight threads, the garbage collector, a parallel stream, andmembarrier. Temurin 25 started onwindows/arm64without the window. Seedocs/systrap-windows.md, sections 3.10 and 6.2. membarrieronhvf,whpx, and Windowssystrap: the global and private expedited commands ofmembarrier(2)returnedEINVAL, andMEMBARRIER_CMD_QUERYreported none of them. The commands now succeed, and the query reports them. A call returns after every guest thread that ran guest code at the call has left the guest or stopped once. Idle threads cost nothing. On Windows (whpxandsystrap), the call also orders the Sentry threads withFlushProcessWriteBuffers. On macOS, the call orders guest code only and does not order a Sentry thread that copies data for a system call in progress. Onwhpx, a running guest task returns fromSwitchonce for each call and the kernel runs it again.hvfand Windowssystrapresume the guest without a return. Linux platforms keep the hostmembarrier.fsckLiterepair of an unlinked inode that a directory names: a power loss between the write of a directory entry and the write of its inode could leave an entry that names an inode with link count zero. The repair kept the count, and the next lookup failed withEUCLEAN. The repair now gives the inode the rebuilt count. Only a mount that runsfsckLitemakes this repair, which a volume without a journal needs after a crash.- Directory link count on Windows
hostfs:statof a directory reported 2 links on NTFS for every directory. It now reports 2 plus the number of subdirectories, as Linux does. Tools such asfinduse that count. - Large
truncateon Windowshostfs: a growth above the free disk space failed withENOSPC, and a size above the NTFS file limit failed withEIO. The growth now leaves a hole, as on Linux, and the size above the limit fails withEFBIG. - brk heap on the Windows
systrapengine (windows/amd64andwindows/arm64):brkstopped after 0 MiB to 111 MiB, at a random place in each process. The engine placed a trampoline island at a random address up to 120 MiB above the code, where the heap grows. The engine now looks below the code first.TestE2EGateETExecBrkGrowthmoves the break of anET_EXECprogram by 1 GiB on every backend. Anarm64Go program at0x10000has no address below its code and keeps the old limit; the Go runtime never callsbrk. - Windows
systrapengine errata (windows/amd64andwindows/arm64): three defects of the engine are fixed.- A guest that wrote code through one view of a memory file and ran it through another view kept running the old code. A write through a second view of a frame now drops the patched copies of that frame in every address space. A frame without a patched copy costs no extra fault.
- On
windows/arm64, a fastsvcoverwrote a livex18of the guest with a stale value, unless the guest setx18withmov x18, xN. Thesvcslot now savesx18first, so everysvckeeps it. A guest whose stack pointer is invalid still gets its system call through the debug path. The patcher no longer rewritesmov x18, xN, which leaves 289 rewritten encodings instead of 320. - On
windows/arm64, everyic ivauof a guest cost one debug stop. The engine now replaces the instruction withnop, andCTR_EL0reportsDICandIDCfor the guest. Guests that obey these bits, such as V8, skip the instruction.
- Windows
systrapengine, writes into running code (windows/amd64andwindows/arm64): two kinds of write into code now have the Linux result.- A store into the page of the storing instruction, or into the frame that the thread executes through a second view, ended with SIGSEGV. The engine now completes the store with a single step, and the page runs the new bytes. Self-modifying code and data in a writable and executable code page work. On
windows/arm64, a store exclusive (stxr,stlxr,stxp,stlxp) into its own page still ends with SIGSEGV. - A write of the Sentry into guest memory left a stale patched copy of the page: the copy-out of
read,pread, andrecvinto a mapped page,writeto a file that a guest maps, andprocess_vm_writev. The memory manager and the memory file now report each such write, and the engine drops the copies of the written frames. The report costs one atomic operation for a write into a frame without a copy, and the other platforms keep an empty hook.
- A store into the page of the storing instruction, or into the frame that the thread executes through a second view, ended with SIGSEGV. The engine now completes the store with a single step, and the page runs the new bytes. Self-modifying code and data in a writable and executable code page work. On
Behavior Changes
fallocatemodes other than 0 reachext4liteandhostfs: the system call returnedENOTSUPfor every mode other than 0 on every file system, soFALLOC_FL_KEEP_SIZEandFALLOC_FL_PUNCH_HOLEnever reached a driver from a guest. The call now forwards the mode to a file description that serves modes, whichext4liteandhostfsdo. Other file systems keepENOTSUP. A mode other than 0 skips theRLIMIT_FSIZEcheck of the system call, because only a call th...
v0.6.10002026110200
This release updates the Sentry to gVisor release-20261102.0 and introduces a new version format. AGENTS.md, section 5.2, defines the format.
Behavior Changes
- Upstream gVisor
release-20261102.0: the Sentry now comes from thegobranch commit1cea747e. That commit is the first merge that contains the release commit7a675ac6. The guest vDSO reads a newCLOCK_BOOTTIMEoffset from the parameter page. - Upstream diff budget:
tools/upstream -mode=auditaccepts a charged share of up to 2.0% of upstream files. The old limit was 1.0%. - Version format: the PATCH field of a release tag joins the digit
1, a three-digit release counter, the upstream release date, and the two-digit upstream release suffix.v0.6.10002026110200is the first release in0.6and contains gVisorrelease-20261102.0.
v0.5.2
This release lets Node.js build TypeScript and JavaScript projects on the Windows amd64 systrap engine. The engine runs when the host has no Windows Hypervisor Platform, or with -platform=systrap.
Bug Fixes
- Node.js on the Windows
amd64systrapengine (issue #24):npm install,tsc,transpileModule(), esbuild, andvite buildended with SIGSEGV (exit code 139), SIGTRAP (exit code 133), or SIGILL (exit code 132) in Node.js 20, 22, and 24. Three engine defects caused this:- The repair of a lost FS base ran the faulting
%fsinstruction again, and Windows could reset the base again first. After eight repairs, the engine reported SIGSEGV. The Sentry now runs the instruction and moves past it, so each repair makes progress. - The engine patched false system call and GS sites in the data that V8 keeps beside its code in writable and executable anonymous memory. V8 then read changed pointers. On
amd64, such pages now run unpatched. A writable segment of an ELF file stays patched. - The scan saw only 15 bytes before a page, and it found a GS instruction inside a
leaof Node.js 20. The scan now starts 64 bytes before the page.
- The repair of a lost FS base ran the faulting