mcpbeat

Add Syscall

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.

2k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
18974
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/google/gvisor --skill add-syscall

What it tells the agent to use

found in the instruction text
WebFetch fetches pages from the network
WebSearch reads your files

The instruction itself

21 sections, as written by the author

Add or extend syscall: $target

You are adding or extending Linux syscall support in gvisor. The target is:

$target

Phase 1: Understand the current state

  • Check the syscall table — search pkg/sentry/syscalls/linux/linux64.go

for the syscall name. Determine:

  • Is it registered? (has a table entry)
  • What is its support level? (Supported, PartiallySupported,

ErrorWithEvent, Error)

  • What handler function is it mapped to?
  • Read the existing implementation (if any) — find the handler in

pkg/sentry/syscalls/linux/sys_*.go. Look for:

  • Which flags/options are already handled
  • Which flags fall through to default: / return EINVAL / ENOSYS
  • The function signature pattern: `func Name(t *kernel.Task, sysno

uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl,

error)`

  • Check existing ABI constants — look in pkg/abi/linux/ for the relevant

constants file (e.g., prctl.go, ioctl.go, socket.go)

  • Read the Linux man page / kernel source — use WebSearch or WebFetch to

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.

Phase 2: Plan the changes

Present the user a summary before writing code:

  • What the target syscall/flag does in Linux
  • What changes are needed in gvisor (list files)
  • Any kernel subsystems that need new support (e.g., new task fields, VFS

operations)

  • Whether this can be a simple implementation or needs deeper infrastructure

Wait for user confirmation before proceeding.

Phase 3: Implement

Follow this checklist — not all steps apply to every change:

A. ABI Constants (pkg/abi/linux/)

  • Add any missing constants to the appropriate file
  • Follow the existing naming convention (e.g., PR_ prefix for prctl,

CLONE_ for clone flags)

  • Add a one-line comment matching the Linux kernel comment style: `// PR_FOO

does bar.`

  • If adding a new struct, use pkg/marshal for user-space copying

B. Syscall Implementation (pkg/sentry/syscalls/linux/sys_*.go)

  • New syscall: Create sys_<name>.go with the handler function. Follow

the standard signature:

    func SyscallName(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
  • New flag on existing syscall: Add a case to the existing switch

statement

  • Use linuxerr package for errors (e.g., linuxerr.EINVAL,

linuxerr.EPERM)

  • Use t.CopyIn* / t.CopyOut* or primitive.Copy* for userspace memory

access

  • Use hostarch.Addr for user pointers: args[N].Pointer()
  • Use args[N].Int(), args[N].Uint(), args[N].Uint64() for scalar

arguments

C. Syscall Table (pkg/sentry/syscalls/linux/linux64.go)

  • New syscall: Change from syscalls.ErrorWithEvent(...) or

syscalls.Error(...) to syscalls.Supported("name", HandlerFunc) or

`syscalls.PartiallySupported("name", HandlerFunc, "note about limitations",

nil)`

  • Improved existing: Update PartiallySupported note to reflect newly

supported options, or upgrade to Supported if fully implemented

  • The table has entries for both AMD64 and ARM64 — update both if the syscall

applies to both architectures

D. BUILD files

  • Add new .go files to srcs in pkg/sentry/syscalls/linux/BUILD
  • Add any new package dependencies to deps
  • Add new ABI files to pkg/abi/linux/BUILD if created

E. Kernel/Sentry Support (if needed)

  • New task-level state → pkg/sentry/kernel/task.go or related
  • New filesystem operations → pkg/sentry/vfs/ or pkg/sentry/fsimpl/
  • New network features → pkg/tcpip/

F. Tests (test/syscalls/linux/)

  • Add or extend C++ tests in test/syscalls/linux/<syscall>.cc
  • Use the gtest framework with gvisor's test utilities:
  • SyscallSucceeds(), SyscallFailsWithErrno(EINVAL), etc. from

test/util/test_util.h

  • ASSERT_NO_ERRNO_AND_VALUE() for operations that return PosixErrorOr
  • Add test to test/syscalls/linux/BUILD if it's a new file
  • Test both the happy path and error cases

Phase 4: Test-driven verification against native Linux

The 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.

Step 1: Run the native test

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'

Step 2: Build the gvisor implementation

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/...

Step 3: Run the gvisor (runsc) test

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.

Iterative workflow

The expected loop is:

  • Write/update the C++ test in test/syscalls/linux/<syscall>.cc
  • Run bazel test //test/syscalls:<syscall>_test_native --test_output=errors
  • If it fails → fix the test (it's a bug in the test, not Linux)
  • Once native passes → implement/update the gvisor handler
  • Build gvisor: bazel build //pkg/sentry/syscalls/linux/...
  • Run `bazel test //test/syscalls:<syscall>_test_runsc_ptrace_shared

--test_output=errors`

  • If it fails → fix the gvisor implementation and repeat from step 5

Key patterns to follow

Error handling

// Return Linux error codes from linuxerr package
return 0, nil, linuxerr.EINVAL
return 0, nil, linuxerr.EPERM
return 0, nil, linuxerr.ENOSYS

Copying data to/from userspace

// 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)

Unimplemented options pattern

// For options you deliberately don't implement, emit an event:
t.Kernel().EmitUnimplementedEvent(t, sysno)
return 0, nil, linuxerr.ENOSYS

Credential checks

creds := t.Credentials()
if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, creds.UserNamespace) {
    return 0, nil, linuxerr.EPERM
}

Important notes

  • Match Linux behavior exactly — check the man page and kernel source for edge

cases

  • gvisor does NOT have a real kernel, so some things (hardware access, kernel

modules) cannot be implemented — stub them with appropriate errors

  • Always handle the default: case in switch statements
  • Use t.Kernel().EmitUnimplementedEvent(t, sysno) before returning ENOSYS

for deliberate non-implementation — this enables tracking of missing

features

  • Prefer returning errors over panicking
  • Consider both amd64 and arm64 when the syscall exists on both architectures

How to use it

Copy the folder

Take google/add-syscall from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

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.