google/add-syscall
> (1) Adding a brand-new syscall that currently returns ENOSYS or is missing from the table. (2) Adding missing flags/options to an existing partially-supported syscall (e.g., a new prctl option, ioctl command, or socket option). Use when asked to implement a syscall, add a flag, or improve compatibility for a specific syscall.
npx skills add https://github.com/google/gvisor --skill add-syscall
$targetYou are adding or extending Linux syscall support in gvisor. The target is:
$target
pkg/sentry/syscalls/linux/linux64.gofor the syscall name. Determine:
Supported, PartiallySupported,ErrorWithEvent, Error)
pkg/sentry/syscalls/linux/sys_*.go. Look for:
default: / return EINVAL / ENOSYSuintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl,
error)`
pkg/abi/linux/ for the relevantconstants file (e.g., prctl.go, ioctl.go, socket.go)
look up the exact Linux behavior for the syscall or flag. This is critical
for correctness. Search for the man page (e.g., man 2 prctl) and/or
relevant kernel source.
Present the user a summary before writing code:
operations)
Wait for user confirmation before proceeding.
Follow this checklist — not all steps apply to every change:
pkg/abi/linux/)PR_ prefix for prctl,CLONE_ for clone flags)
does bar.`
pkg/marshal for user-space copyingpkg/sentry/syscalls/linux/sys_*.go)sys_<name>.go with the handler function. Followthe standard signature:
func SyscallName(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
case to the existing switchstatement
linuxerr package for errors (e.g., linuxerr.EINVAL,linuxerr.EPERM)
t.CopyIn* / t.CopyOut* or primitive.Copy* for userspace memoryaccess
hostarch.Addr for user pointers: args[N].Pointer()args[N].Int(), args[N].Uint(), args[N].Uint64() for scalararguments
pkg/sentry/syscalls/linux/linux64.go)syscalls.ErrorWithEvent(...) orsyscalls.Error(...) to syscalls.Supported("name", HandlerFunc) or
`syscalls.PartiallySupported("name", HandlerFunc, "note about limitations",
nil)`
PartiallySupported note to reflect newlysupported options, or upgrade to Supported if fully implemented
applies to both architectures
.go files to srcs in pkg/sentry/syscalls/linux/BUILDdepspkg/abi/linux/BUILD if createdpkg/sentry/kernel/task.go or relatedpkg/sentry/vfs/ or pkg/sentry/fsimpl/pkg/tcpip/test/syscalls/linux/)test/syscalls/linux/<syscall>.ccSyscallSucceeds(), SyscallFailsWithErrno(EINVAL), etc. fromtest/util/test_util.h
ASSERT_NO_ERRNO_AND_VALUE() for operations that return PosixErrorOrtest/syscalls/linux/BUILD if it's a new fileThe test MUST pass on native Linux first. This ensures the test itself is
correct before running it inside gvisor. Native test targets run the C++ test
binary directly on the host kernel.
The native test target naming convention is:
bazel test //test/syscalls:<syscall>_test_native
For example:
bazel test //test/syscalls:access_test_native
bazel test //test/syscalls:prctl_test_native
bazel test //test/syscalls:eventfd_test_native
The native target is auto-generated by the syscall_test() macro in
test/syscalls/BUILD from the cc_binary in test/syscalls/linux/BUILD. It
runs with --platform=native (directly on the host kernel, no gvisor sandbox).
Run the native test FIRST and iterate until it passes. If the native test
fails, the test itself is buggy — fix the test before touching the gvisor
implementation.
bazel test //test/syscalls:<syscall>_test_native --test_output=errors
To run a specific test case:
bazel test //test/syscalls:<syscall>_test_native --test_output=errors --test_arg=--gtest_filter='TestSuite.TestCase'
After the native test passes, verify the gvisor code compiles:
bazel build //pkg/sentry/syscalls/linux/...
If ABI constants changed:
bazel build //pkg/abi/linux/...
After the native test passes and the gvisor implementation compiles, run the
test under gvisor to verify the implementation is correct:
bazel test //test/syscalls:<syscall>_test_runsc_ptrace_shared --test_output=errors
This test MUST pass. If it fails, the gvisor implementation has a bug — fix the
implementation and re-run until it passes.
The expected loop is:
test/syscalls/linux/<syscall>.ccbazel test //test/syscalls:<syscall>_test_native --test_output=errorsbazel build //pkg/sentry/syscalls/linux/...--test_output=errors`
// Return Linux error codes from linuxerr package
return 0, nil, linuxerr.EINVAL
return 0, nil, linuxerr.EPERM
return 0, nil, linuxerr.ENOSYS
// Copy a single int32 to userspace
_, err := primitive.CopyInt32Out(t, args[1].Pointer(), value)
// Copy a struct from userspace
var s SomeStruct
_, err := s.CopyIn(t, args[0].Pointer())
// Copy a string from userspace
name, err := t.CopyInString(addr, maxLen)
// For options you deliberately don't implement, emit an event:
t.Kernel().EmitUnimplementedEvent(t, sysno)
return 0, nil, linuxerr.ENOSYS
creds := t.Credentials()
if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, creds.UserNamespace) {
return 0, nil, linuxerr.EPERM
}
cases
modules) cannot be implemented — stub them with appropriate errors
default: case in switch statementst.Kernel().EmitUnimplementedEvent(t, sysno) before returning ENOSYSfor deliberate non-implementation — this enables tracking of missing
features
Take google/add-syscall from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.