From d1f3b45b38dce7601fd89dca263d8d1f9b98ae79 Mon Sep 17 00:00:00 2001 From: "B. Blechschmidt" Date: Fri, 10 Mar 2023 00:30:44 +0100 Subject: [PATCH 01/49] Add --pass-fd flag to runsc run and exec This commit implements file descriptor passing from the host to the guest. It implements a --pass-fd flag that can be specified multiple times with FD numbers from the host that will be inserted into the file descriptor table of the guest. --- pkg/sentry/control/proc.go | 71 ++++++++++-- runsc/boot/loader.go | 39 ++++++- runsc/cmd/BUILD | 3 +- runsc/cmd/boot.go | 5 + runsc/cmd/exec.go | 46 +++++++- runsc/cmd/exec_test.go | 25 +++-- runsc/cmd/fd_mapping.go | 84 ++++++++++++++ runsc/cmd/run.go | 28 +++++ runsc/container/console_test.go | 7 +- runsc/container/container.go | 5 + runsc/container/container_test.go | 180 ++++++++++++++++++++++++++++-- runsc/donation/donation.go | 11 ++ runsc/sandbox/sandbox.go | 21 +++- 13 files changed, 485 insertions(+), 40 deletions(-) create mode 100644 runsc/cmd/fd_mapping.go diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go index 89bc60f8f..36bd3e64b 100644 --- a/pkg/sentry/control/proc.go +++ b/pkg/sentry/control/proc.go @@ -18,6 +18,7 @@ import ( "bytes" "encoding/json" "fmt" + "os" "sort" "strings" "text/tabwriter" @@ -44,6 +45,41 @@ type Proc struct { Kernel *kernel.Kernel } +// FilePayload aids to ensure that len(urpc.FilePayload.Files) == len(GuestFDs) +// when instantiated through the NewFDMap helper method. +type FilePayload struct { + // FilePayload is the file payload that is transferred via RPC. + urpc.FilePayload + + // GuestFDs are the file descriptors in the file descriptor map of the + // executed application. They correspond 1:1 to the files in the + // urpc.FilePayload. + GuestFDs []int +} + +// NewFDMap returns a FilePayload that maps file descriptors to files inside +// the executed process. +func NewFDMap(fdMap map[int]*os.File) FilePayload { + files := make([]*os.File, 0, len(fdMap)) + + // Make the map iteration order deterministic for the sake of testing. + // Otherwise, the order is randomized and tests relying on the comparison + // of equality will fail. + guestFDs := make([]int, 0, len(fdMap)) + for key := range fdMap { + guestFDs = append(guestFDs, key) + } + sort.Ints(guestFDs) + + for _, guestFD := range guestFDs { + files = append(files, fdMap[guestFD]) + } + return FilePayload{ + FilePayload: urpc.FilePayload{Files: files}, + GuestFDs: guestFDs, + } +} + // ExecArgs is the set of arguments to exec. type ExecArgs struct { // Filename is the filename to load. @@ -84,7 +120,7 @@ type ExecArgs struct { StdioIsPty bool // FilePayload determines the files to give to the new process. - urpc.FilePayload + FilePayload // ContainerID is the container for the process being executed. ContainerID string @@ -97,7 +133,7 @@ type ExecArgs struct { } // String prints the arguments as a string. -func (args ExecArgs) String() string { +func (args *ExecArgs) String() string { if len(args.Argv) == 0 { return args.Filename } @@ -189,19 +225,15 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI } initArgs.Filename = resolved - fds, err := fd.NewFromFiles(args.Files) + fdMap, err := args.createFDMap() if err != nil { - return nil, 0, nil, fmt.Errorf("duplicating payload files: %w", err) + return nil, 0, nil, fmt.Errorf("creating fd map: %w", err) } defer func() { - for _, fd := range fds { - _ = fd.Close() + for _, hostFD := range fdMap { + _ = hostFD.Close() } }() - fdMap := make(map[int]*fd.FD, len(fds)) - for appFD, hostFD := range fds { - fdMap[appFD] = hostFD - } ttyFile, err := fdimport.Import(ctx, fdTable, args.StdioIsPty, args.KUID, args.KGID, fdMap) if err != nil { return nil, 0, nil, err @@ -404,3 +436,22 @@ func ContainerUsage(kr *kernel.Kernel) map[string]uint64 { } return cusage } + +// createFDMap creates the file descriptor map from the unmarshalled ExecArgs. +func (args *ExecArgs) createFDMap() (map[int]*fd.FD, error) { + if len(args.Files) != len(args.GuestFDs) { + return nil, fmt.Errorf("length of payload files does not match length of file descriptor array") + } + fdMap := make(map[int]*fd.FD, len(args.Files)) + for i, file := range args.Files { + var appFD int + // GuestFDs are the indexes of our FD map. + appFD = args.GuestFDs[i] + hostFD, err := fd.NewFromFile(file) + if err != nil { + return nil, fmt.Errorf("duplicating payload files: %w", err) + } + fdMap[appFD] = hostFD + } + return fdMap, nil +} diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index f597f336d..5641ad23f 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -100,6 +100,9 @@ type containerInfo struct { // stdioFDs contains stdin, stdout, and stderr. stdioFDs []*fd.FD + // passFDs are mappings of user-supplied host to guest file descriptors. + passFDs []fdMapping + // goferFDs are the FDs that attach the sandbox to the gofers. goferFDs []*fd.FD @@ -185,6 +188,21 @@ type execProcess struct { hostTTY *fd.FD } +// fdMapping maps guest to host file descriptors. Guest file descriptors are +// exposed to the application inside the sandbox through the FD table. +type fdMapping struct { + guest int + host *fd.FD +} + +// FDMapping is a helper type to represent a mapping from guest to host file +// descriptors. In contrast to the unexported fdMapping type, it does not imply +// file ownership. +type FDMapping struct { + Guest int + Host int +} + func init() { // Initialize the random number generator. mrand.Seed(gtime.Now().UnixNano()) @@ -210,6 +228,9 @@ type Args struct { // StdioFDs is the stdio for the application. The Loader takes ownership of // these FDs and may close them at any time. StdioFDs []int + // PassFDs are user-supplied FD mappings from host to guest descriptors. + // The Loader takes ownership of these FDs and may close them at any time. + PassFDs []FDMapping // OverlayFilestoreFDs are the FDs to the regular files that will back the // tmpfs upper mount in the overlay mounts. OverlayFilestoreFDs []int @@ -283,6 +304,12 @@ func New(args Args) (*Loader, error) { for _, overlayFD := range args.OverlayFilestoreFDs { info.overlayFilestoreFDs = append(info.overlayFilestoreFDs, fd.New(overlayFD)) } + for _, customFD := range args.PassFDs { + info.passFDs = append(info.passFDs, fdMapping{ + host: fd.New(customFD.Host), + guest: customFD.Guest, + }) + } // Create kernel and platform. p, err := createPlatform(args.Conf, args.Device) @@ -525,6 +552,9 @@ func (l *Loader) Destroy() { for _, f := range l.root.stdioFDs { _ = f.Close() } + for _, f := range l.root.passFDs { + _ = f.host.Close() + } for _, f := range l.root.goferFDs { _ = f.Close() } @@ -815,7 +845,7 @@ func (l *Loader) startSubcontainer(spec *specs.Spec, conf *config.Config, cid st func (l *Loader) createContainerProcess(root bool, cid string, info *containerInfo) (*kernel.ThreadGroup, *host.TTYFileDescription, error) { // Create the FD map, which will set stdin, stdout, and stderr. ctx := info.procArgs.NewContext(l.k) - fdTable, ttyFile, err := createFDTable(ctx, info.spec.Process.Terminal, info.stdioFDs, info.spec.Process.User) + fdTable, ttyFile, err := createFDTable(ctx, info.spec.Process.Terminal, info.stdioFDs, info.passFDs, info.spec.Process.User) if err != nil { return nil, nil, fmt.Errorf("importing fds: %w", err) } @@ -1375,7 +1405,7 @@ func (l *Loader) ttyFromIDLocked(key execID) (*host.TTYFileDescription, error) { return ep.tty, nil } -func createFDTable(ctx context.Context, console bool, stdioFDs []*fd.FD, user specs.User) (*kernel.FDTable, *host.TTYFileDescription, error) { +func createFDTable(ctx context.Context, console bool, stdioFDs []*fd.FD, passFDs []fdMapping, user specs.User) (*kernel.FDTable, *host.TTYFileDescription, error) { if len(stdioFDs) != 3 { return nil, nil, fmt.Errorf("stdioFDs should contain exactly 3 FDs (stdin, stdout, and stderr), but %d FDs received", len(stdioFDs)) } @@ -1385,6 +1415,11 @@ func createFDTable(ctx context.Context, console bool, stdioFDs []*fd.FD, user sp 2: stdioFDs[2], } + // Create the entries for the host files that were passed to our app. + for _, customFD := range passFDs { + fdMap[customFD.guest] = customFD.host + } + k := kernel.KernelFromContext(ctx) fdTable := k.NewFDTable() ttyFile, err := fdimport.Import(ctx, fdTable, console, auth.KUID(user.UID), auth.KGID(user.GID), fdMap) diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 978718c9f..60cac9b0f 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -16,6 +16,7 @@ go_library( "do.go", "events.go", "exec.go", + "fd_mapping.go", "gofer.go", "help.go", "install.go", @@ -62,7 +63,6 @@ go_library( "//pkg/state/statefile", "//pkg/sync", "//pkg/unet", - "//pkg/urpc", "//runsc/boot", "//runsc/cmd/util", "//runsc/config", @@ -105,7 +105,6 @@ go_test( "//pkg/sentry/control", "//pkg/sentry/kernel/auth", "//pkg/test/testutil", - "//pkg/urpc", "//runsc/cmd/util", "//runsc/config", "//runsc/container", diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 3731dc4ab..ed9caeb4f 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -66,6 +66,9 @@ type Boot struct { // provided in that order. stdioFDs intFlags + // passFDs are mappings of user-supplied host to guest file descriptors. + passFDs fdMappings + // applyCaps determines if capabilities defined in the spec should be applied // to the process. applyCaps bool @@ -148,6 +151,7 @@ func (b *Boot) SetFlags(f *flag.FlagSet) { f.IntVar(&b.deviceFD, "device-fd", -1, "FD for the platform device file") f.Var(&b.ioFDs, "io-fds", "list of FDs to connect gofer clients. They must follow this order: root first, then mounts as defined in the spec") f.Var(&b.stdioFDs, "stdio-fds", "list of FDs containing sandbox stdin, stdout, and stderr in that order") + f.Var(&b.passFDs, "pass-fd", "mapping of host to guest FDs. They must be in M:N format. M is the host and N the guest descriptor.") f.Var(&b.overlayFilestoreFDs, "overlay-filestore-fds", "FDs to the regular files that will back the tmpfs upper mount in the overlay mounts.") f.IntVar(&b.userLogFD, "user-log-fd", 0, "file descriptor to write user logs to. 0 means no logging.") f.IntVar(&b.startSyncFD, "start-sync-fd", -1, "required FD to used to synchronize sandbox startup") @@ -348,6 +352,7 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomma Device: os.NewFile(uintptr(b.deviceFD), "platform device"), GoferFDs: b.ioFDs.GetArray(), StdioFDs: b.stdioFDs.GetArray(), + PassFDs: b.passFDs.GetArray(), OverlayFilestoreFDs: b.overlayFilestoreFDs.GetArray(), NumCPU: b.cpuNum, TotalMem: b.totalMem, diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go index bd871d29c..6da5e96a5 100644 --- a/runsc/cmd/exec.go +++ b/runsc/cmd/exec.go @@ -32,7 +32,6 @@ import ( "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/control" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" - "gvisor.dev/gvisor/pkg/urpc" "gvisor.dev/gvisor/runsc/cmd/util" "gvisor.dev/gvisor/runsc/config" "gvisor.dev/gvisor/runsc/console" @@ -58,6 +57,10 @@ type Exec struct { // file descriptor referencing the master end of the console's // pseudoterminal. consoleSocket string + + // passFDs are user-supplied FDs from the host to be exposed to the + // sandboxed app. + passFDs fdMappings } // Name implements subcommands.Command.Name. @@ -101,6 +104,7 @@ func (ex *Exec) SetFlags(f *flag.FlagSet) { f.StringVar(&ex.pidFile, "pid-file", "", "filename that the container pid will be written to") f.StringVar(&ex.internalPidFile, "internal-pid-file", "", "filename that the container-internal pid will be written to") f.StringVar(&ex.consoleSocket, "console-socket", "", "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal") + f.Var(&ex.passFDs, "pass-fd", "file descriptor passed to the container in M:N format, where M is the host and N is the guest descriptor (can be supplied multiple times)") } // Execute implements subcommands.Command.Execute. It starts a process in an @@ -140,6 +144,34 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm log.Infof("Using exec capabilities from container: %+v", e.Capabilities) } + // Create the file descriptor map for the process in the container. + fdMap := map[int]*os.File{ + 0: os.Stdin, + 1: os.Stdout, + 2: os.Stderr, + } + + // Add custom file descriptors to the map. + for _, mapping := range ex.passFDs { + file := os.NewFile(uintptr(mapping.Host), "") + if file == nil { + util.Fatalf("failed to create file from file descriptor %d", mapping.Host) + } + fdMap[mapping.Guest] = file + } + + // Close the underlying file descriptors after we have passed them. + defer func() { + for _, file := range fdMap { + fd := file.Fd() + if file.Close() != nil { + log.Debugf("Failed to close FD %d", fd) + } + } + }() + + e.FilePayload = control.NewFDMap(fdMap) + // containerd expects an actual process to represent the container being // executed. If detach was specified, starts a child in non-detach mode, // write the child's PID to the pid file. So when the container returns, the @@ -330,7 +362,11 @@ func (ex *Exec) argsFromCLI(argv []string, enableRaw bool) (*control.ExecArgs, e ExtraKGIDs: extraKGIDs, Capabilities: caps, StdioIsPty: ex.consoleSocket != "" || console.IsPty(os.Stdin.Fd()), - FilePayload: urpc.FilePayload{[]*os.File{os.Stdin, os.Stdout, os.Stderr}}, + FilePayload: control.NewFDMap(map[int]*os.File{ + 0: os.Stdin, + 1: os.Stdout, + 2: os.Stderr, + }), }, nil } @@ -379,7 +415,11 @@ func argsFromProcess(p *specs.Process, enableRaw bool) (*control.ExecArgs, error ExtraKGIDs: extraKGIDs, Capabilities: caps, StdioIsPty: p.Terminal, - FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}}, + FilePayload: control.NewFDMap(map[int]*os.File{ + 0: os.Stdin, + 1: os.Stdout, + 2: os.Stderr, + }), }, nil } diff --git a/runsc/cmd/exec_test.go b/runsc/cmd/exec_test.go index a1e980d08..4c858911e 100644 --- a/runsc/cmd/exec_test.go +++ b/runsc/cmd/exec_test.go @@ -24,7 +24,6 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/sentry/control" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" - "gvisor.dev/gvisor/pkg/urpc" ) func TestUser(t *testing.T) { @@ -76,10 +75,14 @@ func TestCLIArgs(t *testing.T) { expected: control.ExecArgs{ Argv: []string{"ls", "/"}, WorkingDirectory: "/foo/bar", - FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}}, - KUID: 0, - KGID: 0, - ExtraKGIDs: []auth.KGID{1, 2, 3}, + FilePayload: control.NewFDMap(map[int]*os.File{ + 0: os.Stdin, + 1: os.Stdout, + 2: os.Stderr, + }), + KUID: 0, + KGID: 0, + ExtraKGIDs: []auth.KGID{1, 2, 3}, Capabilities: &auth.TaskCapabilities{ BoundingCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE), EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE), @@ -129,10 +132,14 @@ func TestJSONArgs(t *testing.T) { expected: control.ExecArgs{ Argv: []string{"ls", "/"}, WorkingDirectory: "/foo/bar", - FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}}, - KUID: 0, - KGID: 0, - ExtraKGIDs: []auth.KGID{1, 2, 3}, + FilePayload: control.NewFDMap(map[int]*os.File{ + 0: os.Stdin, + 1: os.Stdout, + 2: os.Stderr, + }), + KUID: 0, + KGID: 0, + ExtraKGIDs: []auth.KGID{1, 2, 3}, Capabilities: &auth.TaskCapabilities{ BoundingCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE), EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE), diff --git a/runsc/cmd/fd_mapping.go b/runsc/cmd/fd_mapping.go new file mode 100644 index 000000000..083d6d187 --- /dev/null +++ b/runsc/cmd/fd_mapping.go @@ -0,0 +1,84 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "strconv" + "strings" + + "gvisor.dev/gvisor/runsc/boot" +) + +// fdMappings can be used with flags that appear multiple times. +type fdMappings []boot.FDMapping + +// String implements flag.Value. +func (i *fdMappings) String() string { + return fmt.Sprintf("%v", *i) +} + +// Get implements flag.Value. +func (i *fdMappings) Get() any { + return i +} + +// GetArray returns array of mappings. +func (i *fdMappings) GetArray() []boot.FDMapping { + return *i +} + +// Set implements flag.Value and appends a mapping from the command line to the +// mappings array. +func (i *fdMappings) Set(s string) error { + split := strings.Split(s, ":") + if len(split) != 2 { + // Split returns a slice of length 1 if its first argument does not + // contain the separator. An additional length check is not necessary. + // In case no separator is used and the argument is a valid integer, we + // assume that host FD and guest FD should be identical. + fd, err := strconv.Atoi(split[0]) + if err != nil { + return fmt.Errorf("invalid flag value: must be an integer or a mapping of format M:N") + } + *i = append(*i, boot.FDMapping{ + Host: fd, + Guest: fd, + }) + return nil + } + + fdHost, err := strconv.Atoi(split[0]) + if err != nil { + return fmt.Errorf("invalid flag host value: %v", err) + } + if fdHost < 0 { + return fmt.Errorf("flag host value must be >= 0: %d", fdHost) + } + + fdGuest, err := strconv.Atoi(split[1]) + if err != nil { + return fmt.Errorf("invalid flag guest value: %v", err) + } + if fdGuest < 0 { + return fmt.Errorf("flag guest value must be >= 0: %d", fdGuest) + } + + *i = append(*i, boot.FDMapping{ + Host: fdHost, + Guest: fdGuest, + }) + return nil +} diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index bbeb610b9..1e6dbe90c 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -16,9 +16,11 @@ package cmd import ( "context" + "os" "github.com/google/subcommands" "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/cmd/util" "gvisor.dev/gvisor/runsc/config" "gvisor.dev/gvisor/runsc/container" @@ -33,6 +35,10 @@ type Run struct { // detach indicates that runsc has to start a process and exit without waiting it. detach bool + + // passFDs are user-supplied FDs from the host to be exposed to the + // sandboxed app. + passFDs fdMappings } // Name implements subcommands.Command.Name. @@ -54,6 +60,7 @@ func (*Run) Usage() string { // SetFlags implements subcommands.Command.SetFlags. func (r *Run) SetFlags(f *flag.FlagSet) { f.BoolVar(&r.detach, "detach", false, "detach from the container's process") + f.Var(&r.passFDs, "pass-fd", "file descriptor passed to the container in M:N format, where M is the host and N is the guest descriptor (can be supplied multiple times)") r.Create.SetFlags(f) } @@ -89,6 +96,26 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomman } specutils.LogSpecDebug(spec, conf.OCISeccomp) + // Create files from file descriptors. + fdMap := make(map[int]*os.File) + for _, mapping := range r.passFDs { + file := os.NewFile(uintptr(mapping.Host), "") + if file == nil { + return util.Errorf("Failed to create file from file descriptor %d", mapping.Host) + } + fdMap[mapping.Guest] = file + } + + // Close the underlying file descriptors after we have passed them. + defer func() { + for _, file := range fdMap { + fd := file.Fd() + if file.Close() != nil { + log.Debugf("Failed to close FD %d", fd) + } + } + }() + runArgs := container.Args{ ID: id, Spec: spec, @@ -97,6 +124,7 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomman PIDFile: r.pidFile, UserLog: r.userLog, Attached: !r.detach, + PassFiles: fdMap, } ws, err := container.Run(conf, runArgs) if err != nil { diff --git a/runsc/container/console_test.go b/runsc/container/console_test.go index d3a08eed2..0c98c9364 100644 --- a/runsc/container/console_test.go +++ b/runsc/container/console_test.go @@ -30,7 +30,6 @@ import ( "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/test/testutil" "gvisor.dev/gvisor/pkg/unet" - "gvisor.dev/gvisor/pkg/urpc" ) // socketPath creates a path inside bundleDir and ensures that the returned @@ -282,9 +281,9 @@ func TestJobControlSignalExec(t *testing.T) { // our PID counts get messed up. Argv: []string{"/bin/bash", "--noprofile", "--norc"}, // Pass the pty replica as FD 0, 1, and 2. - FilePayload: urpc.FilePayload{ - Files: []*os.File{ptyReplica, ptyReplica, ptyReplica}, - }, + FilePayload: control.NewFDMap(map[int]*os.File{ + 0: ptyReplica, 1: ptyReplica, 2: ptyReplica, + }), StdioIsPty: true, } diff --git a/runsc/container/container.go b/runsc/container/container.go index 9d4352935..cb93350b7 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -176,6 +176,10 @@ type Args struct { // // It only applies for the init container. Attached bool + + // PassFiles are user-supplied files from the host to be exposed to the + // sandboxed app. + PassFiles map[int]*os.File } // New creates the container in a new Sandbox process, unless the metadata @@ -292,6 +296,7 @@ func New(conf *config.Config, args Args) (*Container, error) { Cgroup: containerCgroup, Attached: args.Attached, OverlayFilestoreFiles: overlayFilestoreFiles, + PassFiles: args.PassFiles, } sand, err := sandbox.New(conf, sandArgs) if err != nil { diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 7591dfe89..1dec02817 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -42,7 +42,6 @@ import ( "gvisor.dev/gvisor/pkg/sentry/platform" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/test/testutil" - "gvisor.dev/gvisor/pkg/urpc" "gvisor.dev/gvisor/runsc/cgroup" "gvisor.dev/gvisor/runsc/config" "gvisor.dev/gvisor/runsc/flag" @@ -78,9 +77,11 @@ func executeCombinedOutput(conf *config.Config, cont *Container, name string, ar defer r.Close() args := &control.ExecArgs{ - Filename: name, - Argv: append([]string{name}, arg...), - FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, w, w}}, + Filename: name, + Argv: append([]string{name}, arg...), + FilePayload: control.NewFDMap(map[int]*os.File{ + 0: os.Stdin, 1: w, 2: w, + }), } ws, err := cont.executeSync(conf, args) w.Close() @@ -851,9 +852,9 @@ func TestExec(t *testing.T) { _, err = cont.executeSync(conf, &control.ExecArgs{ Argv: []string{"/nonexist"}, - FilePayload: urpc.FilePayload{ - Files: []*os.File{os.NewFile(uintptr(fds[1]), "sock")}, - }, + FilePayload: control.NewFDMap(map[int]*os.File{ + 0: os.NewFile(uintptr(fds[1]), "sock"), + }), }) want := "failed to load /nonexist" if err == nil || !strings.Contains(err.Error(), want) { @@ -2724,3 +2725,168 @@ func TestSandboxCommunicationUnshare(t *testing.T) { t.Errorf("SignalContainer(): %v", err) } } + +// writeAndReadFromPipe writes the bytes to the write end of the pipe, then +// reads from the read end and returns the result. +func writeAndReadFromPipe(write, read *os.File, msg string) (string, error) { + // Write the message to be read by the guest. + if _, err := io.StringWriter(write).WriteString(msg); err != nil { + return "", fmt.Errorf("failed to write message to pipe: %w", err) + } + write.Close() + + // Read and return the message. + response, err := io.ReadAll(read) + if err != nil { + return "", fmt.Errorf("failed to read from pipe: %w", err) + } + read.Close() + + return string(response), nil +} + +func createPipes() (*os.File, *os.File, *os.File, *os.File, func(), error) { + // This is the first pipe which the host writes to and the guest reads + // from. + guestRead, hostWrite, err := os.Pipe() + if err != nil { + return nil, nil, nil, nil, nil, err + } + + // This is the second pipe which the guest writes to and the host reads + // from. + hostRead, guestWrite, err := os.Pipe() + if err != nil { + guestRead.Close() + hostWrite.Close() + return nil, nil, nil, nil, nil, err + } + + cleanup := func() { + guestRead.Close() + hostWrite.Close() + hostRead.Close() + guestWrite.Close() + } + + return guestRead, hostWrite, hostRead, guestWrite, cleanup, nil +} + +// TestFDPassingRun checks that file descriptors passed into a new container +// work as expected. +func TestFDPassingRun(t *testing.T) { + guestRead, hostWrite, hostRead, guestWrite, cleanup, err := createPipes() + if err != nil { + t.Fatalf("error creating pipes: %v", err) + } + defer cleanup() + + // In the guest, read from the host and write the result back to the host. + conf := testutil.TestConfig(t) + cmd := fmt.Sprintf("cat /proc/self/fd/%d > /proc/self/fd/%d", int(guestRead.Fd()), int(guestWrite.Fd())) + spec := testutil.NewSpecWithArgs("bash", "-c", cmd) + + _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer cleanup() + + args := Args{ + ID: testutil.RandomContainerID(), + Spec: spec, + BundleDir: bundleDir, + PassFiles: map[int]*os.File{ + int(guestRead.Fd()): guestRead, + int(guestWrite.Fd()): guestWrite, + }, + } + + cont, err := New(conf, args) + if err != nil { + t.Fatalf("Creating container: %v", err) + } + defer cont.Destroy() + + if err := cont.Start(conf); err != nil { + t.Fatalf("starting container: %v", err) + } + + // We close guestWrite here because it has been passed into the container. + // If we do not close it, we will never see an EOF. + guestWrite.Close() + + msg := "hello" + got, err := writeAndReadFromPipe(hostWrite, hostRead, msg) + if err != nil { + t.Fatal(err) + } + if got != msg { + t.Errorf("got message %q, want %q", got, msg) + } +} + +// TestFDPassingExec checks that file descriptors passed into an already +// running container work as expected. +func TestFDPassingExec(t *testing.T) { + guestRead, hostWrite, hostRead, guestWrite, cleanup, err := createPipes() + if err != nil { + t.Fatalf("error creating pipes: %v", err) + } + defer cleanup() + + conf := testutil.TestConfig(t) + + // We just sleep here because we want to test file descriptor passing + // inside a process executed inside an already running container. + spec := testutil.NewSpecWithArgs("bash", "-c", "sleep infinity") + + _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer cleanup() + + args := Args{ + ID: testutil.RandomContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + + cont, err := New(conf, args) + if err != nil { + t.Fatalf("Creating container: %v", err) + } + defer cont.Destroy() + + if err := cont.Start(conf); err != nil { + t.Fatalf("starting container: %v", err) + } + + // Prepare executing a command in the running container. + cmd := fmt.Sprintf("cat /proc/self/fd/%d > /proc/self/fd/%d", int(guestRead.Fd()), int(guestWrite.Fd())) + execArgs := &control.ExecArgs{ + Argv: []string{"/bin/bash", "-c", cmd}, + FilePayload: control.NewFDMap(map[int]*os.File{ + int(guestRead.Fd()): guestRead, + int(guestWrite.Fd()): guestWrite, + }), + } + + if _, err = cont.Execute(conf, execArgs); err != nil { + t.Fatalf("Failed to execute command: %v", err) + } + + // We close guestWrite here because it has been passed into the container. + // If we do not close it, we will never see an EOF. + guestWrite.Close() + + msg := "hello" + got, err := writeAndReadFromPipe(hostWrite, hostRead, msg) + if err != nil { + t.Fatal(err) + } + if got != msg { + t.Errorf("got message %q, want %q", got, msg) + } +} diff --git a/runsc/donation/donation.go b/runsc/donation/donation.go index cb5632282..f128eafed 100644 --- a/runsc/donation/donation.go +++ b/runsc/donation/donation.go @@ -106,6 +106,17 @@ func (f *Agency) Transfer(cmd *exec.Cmd, nextFD int) int { return nextFD } +// DonateAndTransferCustomFiles sets up the flags for passing file descriptors from the +// host to the sandbox. Making use of the agency is not necessary, +func DonateAndTransferCustomFiles(cmd *exec.Cmd, nextFD int, files map[int]*os.File) int { + for fd, file := range files { + cmd.Args = append(cmd.Args, fmt.Sprintf("--pass-fd=%d:%d", nextFD, fd)) + cmd.ExtraFiles = append(cmd.ExtraFiles, file) + nextFD++ + } + return nextFD +} + // Close closes any files the agency has taken ownership over. func (f *Agency) Close() { for _, file := range f.closePending { diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 1977c5418..139a7b95e 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -241,6 +241,10 @@ type Args struct { // SinkFiles is the an ordered array of files to be used by seccheck sinks // configured from the --pod-init-config file. SinkFiles []*os.File + + // PassFiles are user-supplied files from the host to be exposed to the + // sandboxed app. + PassFiles map[int]*os.File } // New creates the sandbox process. The caller must call Destroy() on the @@ -528,7 +532,17 @@ func (s *Sandbox) NewCGroup() (cgroup.Cgroup, error) { func (s *Sandbox) Execute(conf *config.Config, args *control.ExecArgs) (int32, error) { log.Debugf("Executing new process in container %q in sandbox %q", args.ContainerID, s.ID) - if err := s.configureStdios(conf, args.Files); err != nil { + // Stdios are those files which have an FD <= 2 in the process. We do not + // want the ownership of other files to be changed by configureStdios. + var stdios []*os.File + for i, fd := range args.GuestFDs { + if fd > 2 || i >= len(args.Files) { + continue + } + stdios = append(stdios, args.Files[i]) + } + + if err := s.configureStdios(conf, stdios); err != nil { return 0, err } @@ -927,8 +941,9 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn cmd.Args = append(cmd.Args, "--attached") } - // nextFD must not be used beyond this point. - _ = donations.Transfer(cmd, nextFD) + nextFD = donations.Transfer(cmd, nextFD) + + _ = donation.DonateAndTransferCustomFiles(cmd, nextFD, args.PassFiles) // Add container ID as the last argument. cmd.Args = append(cmd.Args, s.ID) From ee8a6b95848eec3cfaf887503c916317293300bd Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Mon, 13 Mar 2023 16:09:53 -0700 Subject: [PATCH 02/49] `runsc metric-server`: Add `name_` suffix to `pod` and `namespace` labels. PiperOrigin-RevId: 516350308 --- g3doc/user_guide/observability.md | 2 +- pkg/prometheus/prometheus.go | 4 ++-- test/metricclient/metricclient.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/g3doc/user_guide/observability.md b/g3doc/user_guide/observability.md index 249df7866..ad7e2122f 100644 --- a/g3doc/user_guide/observability.md +++ b/g3doc/user_guide/observability.md @@ -265,7 +265,7 @@ non-human-friendly hexadecimal strings. In order to provide more user-friendly labels, the metric server will pick up the `io.kubernetes.cri.sandbox-name` and `io.kubernetes.cri.sandbox-namespace` annotations provided by `containerd`, and automatically add these as labels -(`pod` and `namespace` respectively) for each per-sandbox metric. +(`pod_name` and `namespace_name` respectively) for each per-sandbox metric. ## Metrics exported diff --git a/pkg/prometheus/prometheus.go b/pkg/prometheus/prometheus.go index 270524698..f645065b7 100644 --- a/pkg/prometheus/prometheus.go +++ b/pkg/prometheus/prometheus.go @@ -35,8 +35,8 @@ var timeNow = time.Now // Prometheus label names used to identify each sandbox. const ( SandboxIDLabel = "sandbox" - PodNameLabel = "pod" - NamespaceLabel = "namespace" + PodNameLabel = "pod_name" + NamespaceLabel = "namespace_name" IterationIDLabel = "iteration" ) diff --git a/test/metricclient/metricclient.go b/test/metricclient/metricclient.go index 38719b5d4..8a8b9cd25 100644 --- a/test/metricclient/metricclient.go +++ b/test/metricclient/metricclient.go @@ -360,10 +360,10 @@ func (m MetricData) GetPrometheusContainerInteger(want WantMetric) (int64, time. "sandbox": want.Sandbox, } if want.Pod != "" { - labels["pod"] = want.Pod + labels["pod_name"] = want.Pod } if want.Namespace != "" { - labels["namespace"] = want.Namespace + labels["namespace_name"] = want.Namespace } return m.GetPrometheusInteger(want.Metric, labels) } From 7453ba9edd9441b4c60b54c468fc8b2c8ec7b76b Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Mon, 13 Mar 2023 16:04:12 -0700 Subject: [PATCH 03/49] gopath: switch to the archive mode With the link mode, we see stale files from previous runs in bazel-bin/gopath. --- .buildkite/hooks/pre-command | 2 +- BUILD | 2 +- tools/go_branch.sh | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.buildkite/hooks/pre-command b/.buildkite/hooks/pre-command index 27b3b6df0..2943b0382 100644 --- a/.buildkite/hooks/pre-command +++ b/.buildkite/hooks/pre-command @@ -24,7 +24,7 @@ function install_pkgs() { } install_pkgs make linux-libc-dev graphviz jq curl binutils gnupg gnupg-agent \ gcc pkg-config apt-transport-https ca-certificates \ - software-properties-common rsync kmod systemd + software-properties-common rsync kmod systemd unzip # Install headers, only if available. if test -n "$(apt-cache search --names-only "^linux-headers-$(uname -r)$")"; then diff --git a/BUILD b/BUILD index a63fc75a0..21d5e51c7 100644 --- a/BUILD +++ b/BUILD @@ -120,7 +120,7 @@ build_test( # The files in this tree are symlinks to the true sources. go_path( name = "gopath", - mode = "link", + mode = "archive", deps = [ # Main binaries. # diff --git a/tools/go_branch.sh b/tools/go_branch.sh index 67d808f89..8a82ed0b0 100755 --- a/tools/go_branch.sh +++ b/tools/go_branch.sh @@ -45,13 +45,10 @@ origpwd=$(pwd) othersrc=("go.mod" "go.sum" "AUTHORS" "LICENSE") readonly module origpwd othersrc -# Build a full gopath. Before copying, this scans the generated directory -# and removes broken symbolic links. It's not clear what conditions this bug -# is hit with bazel, but it happens on occasion. +# Build a full gopath. declare -r go_output="${tmp_dir}/output" make build BAZEL_OPTIONS="" TARGETS="//:gopath" -find bazel-bin/gopath/ -xtype l -delete # See above. -rsync --recursive --delete --copy-links bazel-bin/gopath/ "${go_output}" +unzip bazel-bin/gopath.zip -d "${go_output}" # We expect to have an existing go branch that we will use as the basis for this # commit. That branch may be empty, but it must exist. We search for this branch From 9ea4d0f3b9f5a0f13ab36e39967b11ba02082631 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Tue, 14 Mar 2023 11:30:49 -0700 Subject: [PATCH 04/49] netstack: add context to test description PiperOrigin-RevId: 516584185 --- test/syscalls/linux/tcp_socket.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 050ac3e60..59549dddc 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -2259,7 +2259,10 @@ TEST_P(SimpleTcpSocketTest, OnlyAcknowledgeBacklogConnections) { // opportunity where the listener could process another SYN before completing // the delivery that would have filled the accept queue. // - // This test checks that there is no such race. + // This test checks that there is no such race on loopback. On other + // interfaces, where delivery is not synchronous, it is possible for more + // clients to be in the ESTABLISHED state than there are slots in the accept + // queue. std::array, 100> threads; for (auto& thread : threads) { From 6f769780487e7993a894ca965e92c2dee8678f8b Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Tue, 14 Mar 2023 11:44:30 -0700 Subject: [PATCH 05/49] Fix compile errors when building with Android aarch64 PiperOrigin-RevId: 516588240 --- test/syscalls/linux/exec_state_workload.cc | 9 ++++----- test/syscalls/linux/fpsig_fork.cc | 4 ++-- test/syscalls/linux/fpsig_nested.cc | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/test/syscalls/linux/exec_state_workload.cc b/test/syscalls/linux/exec_state_workload.cc index eafdc2bfa..948db99ea 100644 --- a/test/syscalls/linux/exec_state_workload.cc +++ b/test/syscalls/linux/exec_state_workload.cc @@ -26,9 +26,10 @@ #include "absl/strings/numbers.h" -#ifndef ANDROID // Conflicts with existing operator<< on Android. - -// Pretty-print a sigset_t. +// Pretty-print a sigset_t when it is a struct type. +// This is disabled for targets such as Android x86_64, which define sigset_t as +// an integral type, to prevent conflicts with the std library. +template ::value>> std::ostream& operator<<(std::ostream& out, const sigset_t& s) { out << "{ "; @@ -42,8 +43,6 @@ std::ostream& operator<<(std::ostream& out, const sigset_t& s) { return out; } -#endif - // Verify that the signo handler is handler. int CheckSigHandler(uint32_t signo, uintptr_t handler) { struct sigaction sa; diff --git a/test/syscalls/linux/fpsig_fork.cc b/test/syscalls/linux/fpsig_fork.cc index 7326452cb..d786b2c20 100644 --- a/test/syscalls/linux/fpsig_fork.cc +++ b/test/syscalls/linux/fpsig_fork.cc @@ -114,8 +114,8 @@ TEST(FPSigTest, Fork) { "mov x0, %1\n" "mov x1, %2\n" "mov x2, %3\n" - "svc #0\n" ::"r"(__NR_tgkill), - "r"(parent), "r"(parent_tid), "r"(SIGUSR1)); + "svc #0\n" ::"N"(__NR_tgkill), + "r"((uint64_t)parent), "r"((uint64_t)parent_tid), "N"(SIGUSR1)); #endif uint64_t got; diff --git a/test/syscalls/linux/fpsig_nested.cc b/test/syscalls/linux/fpsig_nested.cc index baa913c39..8222cf5ec 100644 --- a/test/syscalls/linux/fpsig_nested.cc +++ b/test/syscalls/linux/fpsig_nested.cc @@ -86,8 +86,8 @@ void sigusr1(int s, siginfo_t* siginfo, void* _uc) { "mov x0, %1\n" "mov x1, %2\n" "mov x2, %3\n" - "svc #0\n" ::"r"(__NR_tgkill), - "r"(pid), "r"(tid), "r"(SIGUSR2)); + "svc #0\n" ::"N"(__NR_tgkill), + "r"((uint64_t)pid), "r"((uint64_t)tid), "N"(SIGUSR2)); #endif // Record value of %xmm0 again to verify that the nested signal handler @@ -142,8 +142,8 @@ TEST(FPSigTest, NestedSignals) { "mov x0, %1\n" "mov x1, %2\n" "mov x2, %3\n" - "svc #0\n" ::"r"(__NR_tgkill), - "r"(pid), "r"(tid), "r"(SIGUSR1)); + "svc #0\n" ::"N"(__NR_tgkill), + "r"((uint64_t)pid), "r"((uint64_t)tid), "N"(SIGUSR1)); #endif uint64_t got; From 2be9de7ea12022574038cf1f6efa2aec384075fc Mon Sep 17 00:00:00 2001 From: Nick Brown Date: Tue, 14 Mar 2023 16:06:42 -0700 Subject: [PATCH 06/49] Increase ICMP poll timeout on Fuchsia We are still seeing flakes in Fuchsia's CQ. Increase the poll timeout by a factor of 10. PiperOrigin-RevId: 516659576 --- test/syscalls/linux/udp_socket.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc index 32d9ed205..da8dafe53 100644 --- a/test/syscalls/linux/udp_socket.cc +++ b/test/syscalls/linux/udp_socket.cc @@ -56,7 +56,7 @@ namespace { size_t IcmpTimeoutMillis() { // Fuchsia's CI infra is susceptible to timing jumps. Set a long timeout // to avoid flakes. - return GvisorPlatform() == Platform::kFuchsia ? 10000 : 1000; + return GvisorPlatform() == Platform::kFuchsia ? 100000 : 1000; } // Fixture for tests parameterized by the address family to use (AF_INET and From 54a66a66395f080dc23998776f6aa557e1748968 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Tue, 14 Mar 2023 15:49:49 -0700 Subject: [PATCH 07/49] gofer: prevent access to the whole proc fs via procSelfFD --- runsc/cmd/gofer.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index cde936cdb..eec5998cc 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -401,6 +401,12 @@ func setupRootFS(spec *specs.Spec, conf *config.Config) error { if err := unix.Mount("runsc-proc", "/proc/proc", "proc", flags|unix.MS_RDONLY, ""); err != nil { util.Fatalf("error mounting proc: %v", err) } + // self/fd is bind-mounted, so that the FD return by + // OpenProcSelfFD() does not allow escapes with walking ".." . + if err := unix.Mount("/proc/proc/self/fd", "/proc/proc/self/fd", + "", unix.MS_RDONLY|unix.MS_BIND|unix.MS_NOEXEC, ""); err != nil { + util.Fatalf("error mounting proc/self/fd: %v", err) + } if err := copyFile("/proc/etc/localtime", "/etc/localtime"); err != nil { log.Warningf("Failed to copy /etc/localtime: %v. UTC timezone will be used.", err) } From 897c03039ee7f97dbccb20d8ac615f2b84cf77bd Mon Sep 17 00:00:00 2001 From: Konstantin Bogomolov Date: Tue, 14 Mar 2023 17:45:21 -0700 Subject: [PATCH 08/49] Implement systrap context queue. This is the initial implementation of the systrap context queue via a ringbuffer in shared memory between stub threads and the sentry. In this new model there is no longer a bound sysmsg thread for every context; instead each subprocess starts with one initial sysmsg thread, which starts polling the context queue for new contexts arriving from the sentry. If the sentry detects that contexts are spending too much time in the context queue without being processed, it will create new sysmsg threads or wake sleeping ones. Tangentially, sysmsg threads will go to sleep if they spend too much time busy looping without new context arrivals. This model does not yet take into account the full load of the host system or even multiple subprocesses in the same sandbox. Multiple overloaded subprocesses are liable to make each other run slower by kicking sysmsg threads more often than they need to; this will be remedied in follow up CLs. PiperOrigin-RevId: 516680504 --- pkg/sentry/platform/systrap/BUILD | 1 + pkg/sentry/platform/systrap/context_queue.go | 86 ++++++ pkg/sentry/platform/systrap/stub_unsafe.go | 17 +- pkg/sentry/platform/systrap/subprocess.go | 272 ++++++++++++++---- .../platform/systrap/subprocess_amd64.go | 66 ++--- .../platform/systrap/subprocess_arm64.go | 26 +- .../platform/systrap/subprocess_unsafe.go | 23 ++ pkg/sentry/platform/systrap/sysmsg/BUILD | 2 + .../systrap/sysmsg/sighandler_amd64.c | 86 ++++-- .../systrap/sysmsg/sighandler_arm64.c | 48 +++- .../systrap/sysmsg/syshandler_amd64.S | 3 + pkg/sentry/platform/systrap/sysmsg/sysmsg.go | 51 +++- pkg/sentry/platform/systrap/sysmsg/sysmsg.h | 28 +- .../platform/systrap/sysmsg/sysmsg_lib.c | 136 +++++++-- .../platform/systrap/sysmsg/sysmsg_offsets.h | 2 +- .../platform/systrap/sysmsg/sysmsg_unsafe.go | 40 +++ pkg/sentry/platform/systrap/sysmsg_thread.go | 4 - pkg/sentry/platform/systrap/systrap.go | 13 +- 18 files changed, 737 insertions(+), 167 deletions(-) create mode 100644 pkg/sentry/platform/systrap/context_queue.go create mode 100644 pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go diff --git a/pkg/sentry/platform/systrap/BUILD b/pkg/sentry/platform/systrap/BUILD index 946beeeb6..bc41e5bf9 100644 --- a/pkg/sentry/platform/systrap/BUILD +++ b/pkg/sentry/platform/systrap/BUILD @@ -22,6 +22,7 @@ go_library( srcs = [ "context_decoupling_disable.go", "context_decoupling_enable.go", + "context_queue.go", "filters.go", "filters_amd64.go", "filters_arm64.go", diff --git a/pkg/sentry/platform/systrap/context_queue.go b/pkg/sentry/platform/systrap/context_queue.go new file mode 100644 index 000000000..985117544 --- /dev/null +++ b/pkg/sentry/platform/systrap/context_queue.go @@ -0,0 +1,86 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package systrap + +import ( + "sync/atomic" +) + +// LINT.IfChange +const ( + // maxEntries is the size of the ringbuffer. + maxContextQueueEntries uint32 = uint32(maxGuestContexts) + 1 +) + +type queuedContext struct { + contextID uint32 + threadID uint32 +} + +// contextQueue is a structure shared with the each stub thread that is used to +// signal to stub threads which contexts are ready to resume running. +// +// It is a lockless ringbuffer where threads try to police themselves on whether +// they should continue waiting for a context or go to sleep if they are +// unneeded. +type contextQueue struct { + // start is an index used for taking contexts out of the ringbuffer. + start uint32 + // end is an index used for putting new contexts into the ringbuffer. + end uint32 + // stubPollingIndex is used by stubs to indicate polling order. + stubPollingIndex uint32 + // stubPollingIndexBase is used by stubs to indicate to each other how many + // threads went to sleep. + stubPollingIndexBase uint32 + // numSleepingThreads indicates to the sentry how many stubs are asleep. + numSleepingThreads uint32 + // ringbuffer is the mmapped region of memory that's shared with the stub + // threads. + ringbuffer [maxContextQueueEntries]uint32 +} + +// LINT.ThenChange(./sysmsg/sysmsg_lib.c) + +func (q *contextQueue) init() { + for i := uint32(0); i < maxContextQueueEntries; i++ { + q.ringbuffer[i] = invalidContextID + } + atomic.StoreUint32(&q.start, 0) + atomic.StoreUint32(&q.end, 0) + atomic.StoreUint32(&q.stubPollingIndex, 0) + atomic.StoreUint32(&q.stubPollingIndexBase, 0) + atomic.StoreUint32(&q.numSleepingThreads, 0) +} + +func (q *contextQueue) isEmpty() bool { + return atomic.LoadUint32(&q.start) == atomic.LoadUint32(&q.end) +} + +func (q *contextQueue) queuedContexts() uint32 { + return (atomic.LoadUint32(&q.end) + maxContextQueueEntries - atomic.LoadUint32(&q.start)) % maxContextQueueEntries +} + +func (q *contextQueue) add(contextID uint32) uint32 { + next := atomic.AddUint32(&q.end, 1) + if (next % maxContextQueueEntries) == + (atomic.LoadUint32(&q.start) % maxContextQueueEntries) { + // should be unreacheable + panic("contextQueue is full") + } + next = (next - 1) % maxContextQueueEntries + atomic.StoreUint32(&q.ringbuffer[next], contextID) + return next // remove me +} diff --git a/pkg/sentry/platform/systrap/stub_unsafe.go b/pkg/sentry/platform/systrap/stub_unsafe.go index d87494ade..2f8aac0d7 100644 --- a/pkg/sentry/platform/systrap/stub_unsafe.go +++ b/pkg/sentry/platform/systrap/stub_unsafe.go @@ -109,6 +109,9 @@ func stubInit() { // |--------stubSysmsgStack-------------| // | Reserved space for per-thread | // | sysmsg stacks. | + // |----------stubContextQueue----------| + // | Shared ringbuffer queue for stubs | + // | to select the next context. | // |--------stubThreadContextRegion-----| // | Reserved space for thread contexts | // *------------------------------------* @@ -135,6 +138,14 @@ func stubInit() { // has to be aligned to sysmsg.PerThreadMemSize. // Look at sysmsg/sighandler.c:sysmsg_addr() for more details. mapLen, _ = hostarch.PageRoundUp(mapLen + sysmsg.PerThreadMemSize*(maxSystemThreads+1)) + + // Allocate context queue region + if contextDecouplingExp { + stubContextQueueRegion = mapLen + stubContextQueueRegionLen, _ = hostarch.PageRoundUp(unsafe.Sizeof(contextQueue{})) + mapLen += stubContextQueueRegionLen + } + // Allocate thread context region stubContextRegion = mapLen stubContextRegionLen = sysmsg.AllocatedSizeofThreadContextStruct * (maxGuestContexts + 1) @@ -177,6 +188,7 @@ func stubInit() { // Randomize stubSysmsgStack address. gap := uintptr(rand.Uint64()) * hostarch.PageSize % (maximumUserAddress - stubStart - mapLen) stubSysmsgStack += uintptr(gap) + stubContextQueueRegion += uintptr(gap) stubContextRegion += uintptr(gap) // Copy the stub to the address. @@ -187,6 +199,7 @@ func stubInit() { stubSysmsgStart += stubStart stubSysmsgStack += stubStart stubROMapEnd += stubStart + stubContextQueueRegion += stubStart stubContextRegion += stubStart // Align stubSysmsgStack to the per-thread stack size. @@ -209,6 +222,8 @@ func stubInit() { exp := (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_decoupling_exp))) if contextDecouplingExp { *exp = 1 + contextQueue := (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_queue_addr))) + *contextQueue = uint64(stubContextQueueRegion) } prepareSeccompRules(stubSysmsgStart, stubSysmsgRules, stubSysmsgRulesLen) @@ -224,7 +239,7 @@ func stubInit() { // Set the end. stubEnd = stubStart + mapLen + uintptr(gap) - log.Debugf("stubStart %x stubSysmsgStart %x stubSysmsgStack %x, stubThreadContextRegion %x, mapLen %x", stubStart, stubSysmsgStart, stubSysmsgStack, stubContextRegion, mapLen) + log.Debugf("stubStart %x stubSysmsgStart %x stubSysmsgStack %x, stubContextQueue %x, stubThreadContextRegion %x, mapLen %x", stubStart, stubSysmsgStart, stubSysmsgStack, stubContextQueueRegion, stubContextRegion, mapLen) log.Debugf(archState.String()) log.Debugf("contextDecouplingExp=%t", contextDecouplingExp) } diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index e596bac43..ca3fa7244 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -19,6 +19,7 @@ import ( "os" "runtime" "sync" + "sync/atomic" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" @@ -97,6 +98,11 @@ type requestStub struct { done chan *thread } +// maxSysmsgThreads specifies the maximum number of system threads that a +// subprocess can create in context decoupled mode. +// TODO(b/268366549): Replace maxSystemThreads below. +var maxSysmsgThreads = runtime.GOMAXPROCS(0) + const ( // maxSystemThreads specifies the maximum number of system threads that a // subprocess may create in order to process the contexts. @@ -119,6 +125,9 @@ type subprocess struct { // requests is used to signal creation of new threads. requests chan any + // sysmsgInitRegs is used to reset sysemu regs. + sysmsgInitRegs arch.Registers + // mu protects the following fields. mu sync.Mutex @@ -147,8 +156,19 @@ type subprocess struct { syscallThreadMu sync.Mutex syscallThread *syscallThread + // sysmsgThreadsMu protects sysmsgThreads and numSysmsgThreads sysmsgThreadsMu sync.Mutex - sysmsgThreads map[uint32]*sysmsgThread + // sysmsgThreads is a collection of all active sysmsg threads in the + // subprocess. + sysmsgThreads map[uint32]*sysmsgThread + // numSysmsgThreads counts the number of active sysmsg threads; we use a + // counter instead of using len(sysmsgThreads) because we need to synchronize + // how many threads get created _before_ the creation happens. + numSysmsgThreads int + + // contextQueue is a queue of all contexts that are ready to switch back to + // user mode. + contextQueue *contextQueue } func (s *subprocess) initSyscallThread(ptraceThread *thread) error { @@ -266,11 +286,12 @@ func newSubprocess(create func() (*thread, error), memoryFile *pgalloc.MemoryFil runtime.LockOSThread() defer runtime.UnlockOSThread() - // Initialize the first thread. + // Initialize the syscall thread. ptraceThread, err := create() if err != nil { return nil, err } + sp.sysmsgInitRegs = ptraceThread.initRegs if err := sp.initSyscallThread(ptraceThread); err != nil { return nil, err @@ -291,6 +312,14 @@ func newSubprocess(create func() (*thread, error), memoryFile *pgalloc.MemoryFil sp.usertrap = usertrap.New() sp.mapSharedRegions() + // Create the initial sysmsg thread. + if contextDecouplingExp { + if _, err := sp.createSysmsgThread(nil, nil, nil); err != nil { + return nil, err + } + sp.numSysmsgThreads++ + } + return sp, nil } @@ -301,7 +330,7 @@ func newSubprocess(create func() (*thread, error), memoryFile *pgalloc.MemoryFil // Should be called before any sysmsg threads are created. // Initializes s.contextQueue and s.threadContextRegion. func (s *subprocess) mapSharedRegions() { - if s.threadContextRegion != 0 { + if s.contextQueue != nil || s.threadContextRegion != 0 { panic("contextQueue or threadContextRegion was already initialized") } @@ -310,6 +339,27 @@ func (s *subprocess) mapSharedRegions() { Dir: pgalloc.TopDown, } + if contextDecouplingExp { + // Map shared regions into the sentry. + contextQueueFR, contextQueue := mmapContextQueueForSentry(s.memoryFile, opts) + contextQueue.init() + + // Map thread context region into the syscall thread. + _, err := s.syscallThread.syscall( + unix.SYS_MMAP, + arch.SyscallArgument{Value: uintptr(stubContextQueueRegion)}, + arch.SyscallArgument{Value: uintptr(contextQueueFR.Length())}, + arch.SyscallArgument{Value: uintptr(unix.PROT_READ | unix.PROT_WRITE)}, + arch.SyscallArgument{Value: uintptr(unix.MAP_SHARED | unix.MAP_FILE | unix.MAP_FIXED)}, + arch.SyscallArgument{Value: uintptr(s.memoryFile.FD())}, + arch.SyscallArgument{Value: uintptr(contextQueueFR.Start)}) + if err != nil { + panic(fmt.Sprintf("failed to mmap context queue region into syscall thread: %v", err)) + } + + s.contextQueue = contextQueue + } + // Map thread context region into the sentry. threadContextFR, err := s.memoryFile.Allocate(uint64(stubContextRegionLen), opts) if err != nil { @@ -327,8 +377,6 @@ func (s *subprocess) mapSharedRegions() { } // Map thread context region into the syscall thread. - // Map shared regions that will be the same and used for all sysmsg threads - // in this subprocess. if _, err := s.syscallThread.syscall( unix.SYS_MMAP, arch.SyscallArgument{Value: uintptr(stubContextRegion)}, @@ -631,18 +679,16 @@ func (t *thread) NotifyInterrupt() { func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool, shouldPatchSyscall bool, err error) { // Reset necessary registers. regs := &ac.StateData().Regs + s.resetSysemuRegs(regs) + ctx := s.getThreadContextFromID(c.cid) + ctx.Regs = regs.PtraceRegs + restoreArchSpecificState(ctx, ac) + + // Get sysmsg thread bound to the context; no-op if contextDecoupling is on. sysThread, err := s.getSysmsgThread(regs, c, ac) if err != nil { return false, false, err } - msg := sysThread.msg - ctx := s.getThreadContextFromID(c.cid) - t := sysThread.thread - t.resetSysemuRegs(regs) - - s.restoreFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) - ctx.Regs = regs.PtraceRegs - restoreArchSpecificState(regs, t, sysThread, msg, ac) // Check for interrupts, and ensure that future interrupts signal the context. if !c.interrupt.Enable(c) { @@ -653,11 +699,46 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool } defer c.interrupt.Disable() - msg.EnableSentryFastPath() - sysThread.waitEvent(sysmsg.ThreadStateDone) + if contextDecouplingExp { + s.restoreFPState(nil, ctx, 0, c, ac) - if msg.Err != 0 { - panic(fmt.Sprintf("stub thread %d failed: err %d line %d: %s", t.tid, msg.Err, msg.Line, msg)) + // Place the context onto the context queue. + ctx.State.Set(sysmsg.ContextStateNone) + s.contextQueue.add(uint32(c.cid)) + s.waitOnState(ctx) + + // Check if there's been an error. + tid := atomic.LoadUint32(&ctx.ThreadID) + if tid != invalidThreadID { + if sysThread, ok := s.sysmsgThreads[tid]; ok && sysThread.msg.Err != 0 { + msg := sysThread.msg + panic(fmt.Sprintf("stub thread %d failed: err 0x%x line %d: %s", sysThread.thread.tid, msg.Err, msg.Line, msg)) + } + log.Warningf("systrap: found unexpected ThreadContext.ThreadID field, expected %d found %d", invalidThreadID, tid) + } + } else { + msg := sysThread.msg + t := sysThread.thread + + s.restoreFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) + + msg.EnableSentryFastPath() + sysThread.waitEvent(sysmsg.ThreadStateDone) + + // Check if there's been an error. + if msg.Err != 0 { + panic(fmt.Sprintf("stub thread %d failed: err %d line %d: %s", t.tid, msg.Err, msg.Line, msg)) + } + + if ctx.State != sysmsg.ContextStateSyscallTrap { + var err error + sysThread.fpuStateToMsgOffset, err = msg.FPUStateOffset() + if err != nil { + return false, false, err + } + } + + retrieveArchSpecificState(ctx, ac) } regs.PtraceRegs = ctx.Regs @@ -665,16 +746,6 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool // either delivered from the kernel or from this process. We // don't respect other signals. c.signalInfo = ctx.SignalInfo - if !contextDecouplingExp && ctx.State != sysmsg.ContextStateSyscallTrap { - var err error - sysThread.fpuStateToMsgOffset, err = msg.FPUStateOffset() - if err != nil { - return false, false, err - } - } - - retrieveArchSpecificState(regs, msg, t, ac) - if ctx.State == sysmsg.ContextStateSyscallCanBePatched { ctx.State = sysmsg.ContextStateSyscall shouldPatchSyscall = true @@ -693,6 +764,80 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool return false, false, nil } +const ( + // deepSleepTimeout is the timeout after which we stop polling and fall asleep. + // The value is 100µs for 2GHz CPU. + decoupledDeepSleepTimeout = uint64(200000) + // threadKickTimeout is the timeout after which we will either wake up a sleeping + // thread or create a new one. + threadKickTimeout = uint64(20000) +) + +func (s *subprocess) waitOnState(ctx *sysmsg.ThreadContext) { + // ackedEvents is always reset to 0 at the end of this function. + ackedEvents := uint32(0) + kicked := false + slowPath := false + start := cputicks() + handshake := false + for curState := ctx.State.Get(); curState == sysmsg.ContextStateNone; curState = ctx.State.Get() { + if !slowPath { + delta := uint64(cputicks() - start) + if delta > decoupledDeepSleepTimeout { + ctx.DisableSentryFastPath() + slowPath = true + continue + } + + if !handshake && ackedEvents != atomic.LoadUint32(&ctx.Acked) { + handshake = true + continue + } + spinloop() + } else { + // If the context already received a handshake then it knows it's being + // worked on. + if !kicked && !handshake { + kicked = true + s.kickSysmsgThread() + } + + ctx.SleepOnState(curState) + } + } + + atomic.StoreUint32(&ctx.Acked, 0) + ctx.EnableSentryFastPath() +} + +func (s *subprocess) kickSysmsgThread() { + s.sysmsgThreadsMu.Lock() + + if atomic.LoadUint32(&s.contextQueue.numSleepingThreads) > 0 { + for _, t := range s.sysmsgThreads { + if t.msg.State.Get() == sysmsg.ThreadStateAsleep { + t.msg.WakeSysmsgThread() + s.sysmsgThreadsMu.Unlock() + return + } + } + } + // It's also possible that we got here after iterating through all other + // threads and not finding anything asleep because other goroutines already + // woke up every other thread up. + if s.numSysmsgThreads < maxSysmsgThreads { + s.numSysmsgThreads++ + s.sysmsgThreadsMu.Unlock() + if _, err := s.createSysmsgThread(nil, nil, nil); err != nil { + s.sysmsgThreadsMu.Lock() + s.numSysmsgThreads-- + s.sysmsgThreadsMu.Unlock() + } + } else { + s.sysmsgThreadsMu.Unlock() + } +} + // syscall executes the given system call without handling interruptions. func (s *subprocess) syscall(sysno uintptr, args ...arch.SyscallArgument) (uintptr, error) { s.syscallThreadMu.Lock() @@ -753,16 +898,24 @@ func (s *subprocess) PullFullState(c *context, ac *arch.Context64) error { panic("Attempted to PullFullState for context that is not used in subprocess") } ctx := s.getThreadContextFromID(c.cid) - sysThread, err := s.getSysmsgThread(&ac.StateData().Regs, c, ac) - if err != nil { - return err + if contextDecouplingExp { + s.saveFPState(nil, ctx, 0, c, ac) + } else { + sysThread, err := s.getSysmsgThread(&ac.StateData().Regs, c, ac) + if err != nil { + return err + } + s.saveFPState(sysThread.msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) } - s.saveFPState(sysThread.msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) return nil } // getSysmsgThread returns a sysmsg thread for the specified context. +// (Unused if contextDecouplingExp=true). func (s *subprocess) getSysmsgThread(tregs *arch.Registers, c *context, ac *arch.Context64) (*sysmsgThread, error) { + if contextDecouplingExp { + return nil, nil + } sysThread := c.sysmsgThread if sysThread != nil && sysThread.subproc != s { // This can happen if a new address space @@ -773,6 +926,19 @@ func (s *subprocess) getSysmsgThread(tregs *arch.Registers, c *context, ac *arch if sysThread != nil { return sysThread, nil } + return s.createSysmsgThread(tregs, c, ac) +} + +// createSysmsgThread creates a new sysmsg thread. +// If contextDecouplingExp=false, the thread starts working on the given context. +// Otherwise the given function parameters are not used, and the thread starts +// processing any available context in the context queue. +func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *arch.Context64) (*sysmsgThread, error) { + if contextDecouplingExp { + // We will not bind any specific context to this thread. We will still use + // tregs to setup the thread though. + tregs = &arch.Registers{} + } // Create a new seccomp process. var r requestThread @@ -803,12 +969,14 @@ func (s *subprocess) getSysmsgThread(tregs *arch.Registers, c *context, ac *arch // TODO(b/144063246): Need to fail the clone system call. panic(fmt.Sprintf("failed to allocate a new stack: %v", err)) } - sysThread = &sysmsgThread{ + sysThread := &sysmsgThread{ thread: p, subproc: s, stackRange: fr, } - tid := uint32(p.tid) + // Use the sysmsgStackID as a handle on this thread instead of host tid in + // order to be able to reliably specify invalidThreadID. + threadID := uint32(p.sysmsgStackID) // Map the stack into the sentry. sentryStackAddr, _, errno := unix.RawSyscall6( @@ -851,15 +1019,19 @@ func (s *subprocess) getSysmsgThread(tregs *arch.Registers, c *context, ac *arch } sysThread.setMsg(sysmsg.StackAddrToMsg(sentryStackAddr)) - sysThread.msg.Init(tid) - s.getThreadContextFromID(c.cid).ThreadID = tid - sysThread.msg.ContextID = c.cid + sysThread.msg.Init(threadID) + if contextDecouplingExp { + sysThread.msg.ContextID = uint64(invalidContextID) + } else { + s.getThreadContextFromID(c.cid).ThreadID = threadID + sysThread.msg.ContextID = c.cid + } sysThread.msg.Self = uint64(sysmsgStackAddr + sysmsg.MsgOffsetFromSharedStack) sysThread.msg.SyshandlerStack = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr())) sysThread.msg.ContextRegion = uint64(stubContextRegion) sysThread.msg.Syshandler = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_syshandler)) - sysThread.msg.State.Set(sysmsg.ThreadStateDone) + sysThread.msg.State.Set(sysmsg.ThreadStateInitializing) // Install a pre-compiled seccomp rules for the BPF process. _, err = p.syscallIgnoreInterrupt(&p.initRegs, unix.SYS_PRCTL, @@ -879,15 +1051,12 @@ func (s *subprocess) getSysmsgThread(tregs *arch.Registers, c *context, ac *arch } // Prepare to start the BPF process. - p.resetSysemuRegs(tregs) - archSpecificSysThreadInit(sysThread, tregs) + s.resetSysemuRegs(tregs) + setArchSpecificRegs(sysThread, tregs) if err := p.setRegs(tregs); err != nil { panic(fmt.Sprintf("ptrace set regs failed: %v", err)) } - // Send a fake event to stop the BPF process. - if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(p.tgid), uintptr(p.tid), uintptr(unix.SIGSEGV)); e != 0 { - panic(fmt.Sprintf("tkill failed: %v", e)) - } + archSpecificSysmsgThreadInit(sysThread) // Skip SIGSTOP. if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(p.tgid), uintptr(p.tid), uintptr(unix.SIGCONT)); e != 0 { panic(fmt.Sprintf("tkill failed: %v", e)) @@ -897,23 +1066,23 @@ func (s *subprocess) getSysmsgThread(tregs *arch.Registers, c *context, ac *arch panic(fmt.Sprintf("can't detach new clone: %v", errno)) } - sysThread.waitEvent(sysmsg.ThreadStateNone) - if msg := sysThread.msg; msg.Err != 0 { - panic(fmt.Sprintf("stub thread failed: %v (line %v)", msg.Err, msg.Line)) - } - if !contextDecouplingExp { + sysThread.waitEvent(sysmsg.ThreadStateNone) + if msg := sysThread.msg; msg.Err != 0 { + panic(fmt.Sprintf("stub thread failed: %v (line %v)", msg.Err, msg.Line)) + } + sysThread.fpuStateToMsgOffset, err = sysThread.msg.FPUStateOffset() if err != nil { sysThread.destroy() return nil, err } + + c.sysmsgThread = sysThread } - c.sysmsgThread = sysThread - s.sysmsgThreadsMu.Lock() - s.sysmsgThreads[tid] = sysThread + s.sysmsgThreads[threadID] = sysThread s.sysmsgThreadsMu.Unlock() return sysThread, nil @@ -960,6 +1129,7 @@ func (s *subprocess) registerContext(c *context) error { s.IncRef() c.cid = id c.subprocess = s + c.FullStateChanged() unlock() threadContext := s.getThreadContextFromID(id) diff --git a/pkg/sentry/platform/systrap/subprocess_amd64.go b/pkg/sentry/platform/systrap/subprocess_amd64.go index 14b8fb076..b6d69a0ca 100644 --- a/pkg/sentry/platform/systrap/subprocess_amd64.go +++ b/pkg/sentry/platform/systrap/subprocess_amd64.go @@ -19,7 +19,6 @@ package systrap import ( "fmt" - "runtime" "strings" "golang.org/x/sys/unix" @@ -37,13 +36,13 @@ const ( // resetSysemuRegs sets up emulation registers. // // This should be called prior to calling sysemu. -func (t *thread) resetSysemuRegs(regs *arch.Registers) { - regs.Cs = t.initRegs.Cs - regs.Ss = t.initRegs.Ss - regs.Ds = t.initRegs.Ds - regs.Es = t.initRegs.Es - regs.Fs = t.initRegs.Fs - regs.Gs = t.initRegs.Gs +func (s *subprocess) resetSysemuRegs(regs *arch.Registers) { + regs.Cs = s.sysmsgInitRegs.Cs + regs.Ss = s.sysmsgInitRegs.Ss + regs.Ds = s.sysmsgInitRegs.Ds + regs.Es = s.sysmsgInitRegs.Es + regs.Fs = s.sysmsgInitRegs.Fs + regs.Gs = s.sysmsgInitRegs.Gs } // createSyscallRegs sets up syscall registers. @@ -211,38 +210,31 @@ func appendArchSeccompRules(rules []seccomp.RuleSet) []seccomp.RuleSet { }...) } -func restoreArchSpecificState(regs *arch.Registers, t *thread, sysThread *sysmsgThread, msg *sysmsg.Msg, _ *arch.Context64) { - regs.Gs_base = msg.Self +func restoreArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) { +} - // Switching gs_base is a rare operation, therefore checking that we need to do - // so is better done in the sentry, because doing so on a host that doesn't - // have FSGSBASE instructions enabled is quite expensive since it would require - // an ARCH_PRCTL syscall. - if regs.Gs_base != sysThread.gsBase { - runtime.LockOSThread() - defer runtime.UnlockOSThread() +func setArchSpecificRegs(sysThread *sysmsgThread, regs *arch.Registers) { + if contextDecouplingExp { + // Set the start function and initial stack. + regs.PtraceRegs.Rip = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_start)) + regs.PtraceRegs.Rsp = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr())) + } - t.attach() + // Set gs_base; this is the only time we set it and we don't expect it to ever + // change for any thread. + regs.Gs_base = sysThread.msg.Self +} - var r arch.Registers - if err := t.getRegs(&r); err != nil { - panic(fmt.Sprintf("ptrace get regs failed: %v", err)) +func retrieveArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) { +} + +func archSpecificSysmsgThreadInit(sysThread *sysmsgThread) { + // Send a fake event to stop the BPF process so that it enters the sighandler. + // If there is no coupled context we don't want that to happen because the + // thread needs to find a context first. + if !contextDecouplingExp { + if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(sysThread.thread.tgid), uintptr(sysThread.thread.tid), uintptr(unix.SIGSEGV)); e != 0 { + panic(fmt.Sprintf("tkill failed: %v", e)) } - r.Gs_base = regs.Gs_base - if err := t.setRegs(&r); err != nil { - panic(fmt.Sprintf("ptrace set regs failed: %v", err)) - } - if _, _, errno := unix.RawSyscall6(unix.SYS_PTRACE, unix.PTRACE_DETACH, uintptr(t.tid), 0, 0, 0, 0); errno != 0 { - panic(fmt.Sprintf("ptrace detach failed: %v", errno)) - } - sysThread.gsBase = regs.Gs_base } } - -func archSpecificSysThreadInit(sysThread *sysmsgThread, regs *arch.Registers) { - regs.Gs_base = sysThread.msg.Self - sysThread.gsBase = regs.Gs_base -} - -func retrieveArchSpecificState(regs *arch.Registers, msg *sysmsg.Msg, _ *thread, ac *arch.Context64) { -} diff --git a/pkg/sentry/platform/systrap/subprocess_arm64.go b/pkg/sentry/platform/systrap/subprocess_arm64.go index ae6f0bf9f..8d0d1ff9e 100644 --- a/pkg/sentry/platform/systrap/subprocess_arm64.go +++ b/pkg/sentry/platform/systrap/subprocess_arm64.go @@ -36,7 +36,7 @@ const ( // resetSysemuRegs sets up emulation registers. // // This should be called prior to calling sysemu. -func (t *thread) resetSysemuRegs(regs *arch.Registers) { +func (s *subprocess) resetSysemuRegs(regs *arch.Registers) { } // createSyscallRegs sets up syscall registers. @@ -187,15 +187,27 @@ func (s *subprocess) arm64SyscallWorkaround(t *thread, regs *arch.Registers) { } } -func restoreArchSpecificState(regs *arch.Registers, t *thread, _ *sysmsgThread, msg *sysmsg.Msg, ac *arch.Context64) { - msg.TLS = uint64(ac.TLS()) +func restoreArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) { + ctx.TLS = uint64(ac.TLS()) } -func archSpecificSysThreadInit(sysThread *sysmsgThread, regs *arch.Registers) { +func setArchSpecificRegs(sysThread *sysmsgThread, regs *arch.Registers) { + if contextDecouplingExp { + // Set the start function and initial stack. + regs.PtraceRegs.Pc = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_start)) + regs.PtraceRegs.Sp = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr())) + } } -func retrieveArchSpecificState(regs *arch.Registers, msg *sysmsg.Msg, t *thread, ac *arch.Context64) { - if !ac.SetTLS(uintptr(msg.TLS)) { - panic(fmt.Sprintf("ac.SetTLS(%+v) failed", msg.TLS)) +func retrieveArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) { + if !ac.SetTLS(uintptr(ctx.TLS)) { + panic(fmt.Sprintf("ac.SetTLS(%+v) failed", ctx.TLS)) + } +} + +func archSpecificSysmsgThreadInit(sysThread *sysmsgThread) { + // Send a fake event to stop the BPF process so that it enters the sighandler. + if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(sysThread.thread.tgid), uintptr(sysThread.thread.tid), uintptr(unix.SIGSEGV)); e != 0 { + panic(fmt.Sprintf("tkill failed: %v", e)) } } diff --git a/pkg/sentry/platform/systrap/subprocess_unsafe.go b/pkg/sentry/platform/systrap/subprocess_unsafe.go index 5db549ccb..fadc0a3f8 100644 --- a/pkg/sentry/platform/systrap/subprocess_unsafe.go +++ b/pkg/sentry/platform/systrap/subprocess_unsafe.go @@ -22,8 +22,12 @@ package systrap import ( + "fmt" "unsafe" + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/sentry/memmap" + "gvisor.dev/gvisor/pkg/sentry/pgalloc" "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" ) @@ -45,3 +49,22 @@ func (s *subprocess) getThreadContextFromID(cid uint64) *sysmsg.ThreadContext { tcSlot := s.threadContextRegion + uintptr(cid)*sysmsg.AllocatedSizeofThreadContextStruct return (*sysmsg.ThreadContext)(unsafe.Pointer(tcSlot)) } + +func mmapContextQueueForSentry(memoryFile *pgalloc.MemoryFile, opts pgalloc.AllocOpts) (memmap.FileRange, *contextQueue) { + fr, err := memoryFile.Allocate(uint64(stubContextQueueRegionLen), opts) + if err != nil { + panic(fmt.Sprintf("failed to allocate a new subprocess context memory region")) + } + addr, _, errno := unix.RawSyscall6( + unix.SYS_MMAP, + 0, + uintptr(fr.Length()), + unix.PROT_WRITE|unix.PROT_READ, + unix.MAP_SHARED|unix.MAP_FILE, + uintptr(memoryFile.FD()), uintptr(fr.Start)) + if errno != 0 { + panic(fmt.Sprintf("mmap failed for subprocess context memory region: %v", errno)) + } + + return fr, (*contextQueue)(unsafe.Pointer(addr)) +} diff --git a/pkg/sentry/platform/systrap/sysmsg/BUILD b/pkg/sentry/platform/systrap/sysmsg/BUILD index 44ac4fc54..ba50cccea 100644 --- a/pkg/sentry/platform/systrap/sysmsg/BUILD +++ b/pkg/sentry/platform/systrap/sysmsg/BUILD @@ -118,6 +118,7 @@ go_library( "sysmsg.go", "sysmsg_amd64.go", "sysmsg_arm64.go", + "sysmsg_unsafe.go", ":sighandler_go_arch", ], embedsrcs = [ @@ -130,5 +131,6 @@ go_library( "//pkg/cpuid", "//pkg/errors", "//pkg/hostarch", + "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c b/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c index c42d12399..aa4aca9dd 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c +++ b/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c @@ -54,15 +54,6 @@ long sys_futex(uint32_t *addr, int op, int val, struct __kernel_timespec *tv, (long)addr2, (long)val3); } -void check_sysmsg_thread_context(struct sysmsg *sysmsg, - struct thread_context **ctx) { - struct thread_context *new_ctx = thread_context_addr(sysmsg); - if (*ctx != new_ctx) { - *ctx = new_ctx; - __atomic_store_n(&(*ctx)->fpstate_changed, 1, __ATOMIC_RELEASE); - } -} - union csgsfs { uint64_t csgsfs; // REG_CSGSFS struct { @@ -164,19 +155,61 @@ static void set_fsbase(struct user_regs_struct *ptregs) { } } +// switch_context_amd64 is a wrapper of switch_context() which does checks +// specific to amd64. +struct thread_context *switch_context_amd64( + struct sysmsg *sysmsg, struct thread_context *ctx, + enum thread_state new_thread_state, enum context_state new_context_state) { + get_fsbase(&ctx->ptregs); + long fs_base = ctx->ptregs.fs_base; + + for (;;) { + // TODO(b/271631387): Once stub code globals can be used between objects + // move this check into sysmsg_lib:switch_context(). + if (__export_context_decoupling_exp) { + ctx = switch_context(sysmsg, ctx, new_context_state); + } else { + ctx->state = new_context_state; + wait_state(sysmsg, new_thread_state); + } + + if (__atomic_load_n(&ctx->interrupt, __ATOMIC_ACQUIRE) != 0) { + // This context got interrupted while it was waiting in the queue. + // Setup all the necessary bits to let the sentry know this context has + // switched back because of it. + __atomic_store_n(&ctx->interrupt, 0, __ATOMIC_RELEASE); + new_context_state = CONTEXT_STATE_FAULT; + ctx->signo = SIGCHLD; + ctx->siginfo.si_signo = SIGCHLD; + ctx->ptregs.orig_rax = -1; + } else { + break; + } + } + if (fs_base != ctx->ptregs.fs_base) { + set_fsbase(&ctx->ptregs); + } + return ctx; +} + void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { ucontext_t *ucontext = _ucontext; void *sp = sysmsg_sp(); struct sysmsg *sysmsg = sysmsg_addr(sp); if (sysmsg != sysmsg->self) panic(0xdeaddead); + int32_t thread_state = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE); + if (__export_context_decoupling_exp && + thread_state == THREAD_STATE_INITIALIZING) { + // This thread was interrupted before it even had a context. + return; + } + struct thread_context *ctx = thread_context_addr(sysmsg); if (signo == SIGCHLD) { // If the current thread is in syshandler, an interrupt has to be postponed, // because sysmsg can't be changed. - int32_t thread_state; - thread_state = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE); if (thread_state != THREAD_STATE_NONE) { // There are two possibilities for when we received the interrupt: // 1. Before syshandler switched to the sentry. @@ -221,6 +254,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { return; } + enum context_state ctx_state = CONTEXT_STATE_INVALID; ctx->signo = signo; ctx->siginfo = *siginfo; gregs_to_ptregs(ucontext, &ctx->ptregs); @@ -237,7 +271,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { case SIGSYS: { int si_sysno = siginfo->si_syscall; int i; - ctx->state = CONTEXT_STATE_SYSCALL; + ctx_state = CONTEXT_STATE_SYSCALL; // Check whether this syscall can be replaced on a function call or not. // If a syscall instruction set is "mov sysno, %eax, syscall", it can be @@ -292,7 +326,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { if (need_trap) { // This syscall can be replaced on the function call. - ctx->state = CONTEXT_STATE_SYSCALL_NEED_TRAP; + ctx_state = CONTEXT_STATE_SYSCALL_NEED_TRAP; } } ctx->ptregs.orig_rax = ctx->ptregs.rax; @@ -310,19 +344,14 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { case SIGTRAP: case SIGILL: ctx->ptregs.orig_rax = -1; - ctx->state = CONTEXT_STATE_FAULT; + ctx_state = CONTEXT_STATE_FAULT; break; default: return; } - get_fsbase(&ctx->ptregs); - long fs_base = ctx->ptregs.fs_base; - wait_state(sysmsg, THREAD_STATE_EVENT); + ctx = switch_context_amd64(sysmsg, ctx, THREAD_STATE_EVENT, ctx_state); - if (fs_base != __atomic_load_n(&ctx->ptregs.fs_base, __ATOMIC_ACQUIRE)) { - set_fsbase(&ctx->ptregs); - } if (__export_context_decoupling_exp && __atomic_load_n(&ctx->fpstate_changed, __ATOMIC_ACQUIRE)) { memcpy((uint8_t *)ucontext->uc_mcontext.fpregs, ctx->fpstate, @@ -342,22 +371,23 @@ void __syshandler() { struct thread_context *ctx = thread_context_addr(sysmsg); - ctx->state = CONTEXT_STATE_SYSCALL_TRAP; + enum context_state ctx_state = CONTEXT_STATE_SYSCALL_TRAP; ctx->signo = SIGSYS; ctx->siginfo.si_addr = 0; ctx->siginfo.si_syscall = ctx->ptregs.rax; ctx->ptregs.rax = (unsigned long)-ENOSYS; __atomic_store_n(&sysmsg->interrupt, 0, __ATOMIC_RELAXED); - get_fsbase(&ctx->ptregs); - long fs_base = ctx->ptregs.fs_base; + switch_context_amd64(sysmsg, ctx, THREAD_STATE_EVENT, ctx_state); +} - state = wait_state(sysmsg, THREAD_STATE_EVENT); +// asm_restore_state is implemented in syshandler_amd64.S +void asm_restore_state(); - // Restore state - if (fs_base != ctx->ptregs.fs_base) { - set_fsbase(&ctx->ptregs); - } +// On x86 restore_state jumps straight to user code and does not return. +void restore_state(struct sysmsg *sysmsg, struct thread_context *ctx, void *) { + set_fsbase(&ctx->ptregs); + asm_restore_state(); } void verify_offsets_amd64() { diff --git a/pkg/sentry/platform/systrap/sysmsg/sighandler_arm64.c b/pkg/sentry/platform/systrap/sysmsg/sighandler_arm64.c index 1fe39d11e..ed3c0b036 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sighandler_arm64.c +++ b/pkg/sentry/platform/systrap/sysmsg/sighandler_arm64.c @@ -96,8 +96,17 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { struct sysmsg *sysmsg = sysmsg_addr(sp); if (sysmsg != sysmsg->self) panic(0xdeaddead); + int32_t thread_state = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE); + if (__export_context_decoupling_exp && + thread_state == THREAD_STATE_INITIALIZING) { + // Find a new context and exit to restore it. + __export_start(sysmsg, _ucontext); + return; + } + struct thread_context *ctx = thread_context_addr(sysmsg); + uint32_t ctx_state = CONTEXT_STATE_INVALID; ctx->signo = signo; gregs_to_ptregs(ucontext, &ctx->ptregs); @@ -118,11 +127,11 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { } else { sysmsg->fpstate = (uint64_t)(fpStatePointer) - (uint64_t)sysmsg; } - sysmsg->tls = get_tls(); + ctx->tls = get_tls(); ctx->siginfo = *siginfo; switch (signo) { case SIGSYS: { - ctx->state = CONTEXT_STATE_SYSCALL; + ctx_state = CONTEXT_STATE_SYSCALL; if (siginfo->si_arch != AUDIT_ARCH_AARCH64) { // gVisor doesn't support x32 system calls, so let's change the syscall // number so that it returns ENOSYS. The value added here is just a @@ -138,19 +147,48 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { case SIGFPE: case SIGTRAP: case SIGILL: - ctx->state = CONTEXT_STATE_FAULT; + ctx_state = CONTEXT_STATE_FAULT; break; default: return; } - wait_state(sysmsg, THREAD_STATE_EVENT); + for (;;) { + if (__export_context_decoupling_exp) { + ctx = switch_context(sysmsg, ctx, ctx_state); + } else { + ctx->state = ctx_state; + wait_state(sysmsg, THREAD_STATE_EVENT); + } + + if (__atomic_load_n(&ctx->interrupt, __ATOMIC_ACQUIRE) != 0) { + // This context got interrupted while it was waiting in the queue. + // Setup all the necessary bits to let the sentry know this context has + // switched back because of it. + __atomic_store_n(&ctx->interrupt, 0, __ATOMIC_RELEASE); + ctx_state = CONTEXT_STATE_FAULT; + ctx->signo = SIGCHLD; + ctx->siginfo.si_signo = SIGCHLD; + } else { + break; + } + } + restore_state(sysmsg, ctx, _ucontext); +} + +// On ARM restore_state sets up a correct restore from the sighandler by +// populating _ucontext. +void restore_state(struct sysmsg *sysmsg, struct thread_context *ctx, + void *_ucontext) { + ucontext_t *ucontext = _ucontext; + struct fpsimd_context *fpctx = &ucontext->uc_mcontext.__reserved; + uint8_t *fpStatePointer = (uint8_t *)&fpctx->fpsr; if (__export_context_decoupling_exp && __atomic_load_n(&ctx->fpstate_changed, __ATOMIC_ACQUIRE)) { memcpy(fpStatePointer, ctx->fpstate, __export_arch_state.fp_len); } ptregs_to_gregs(ucontext, &ctx->ptregs); - set_tls(sysmsg->tls); + set_tls(ctx->tls); __atomic_store_n(&sysmsg->state, THREAD_STATE_NONE, __ATOMIC_RELEASE); } diff --git a/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S b/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S index e66b5f908..d5891e5cb 100644 --- a/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S +++ b/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S @@ -196,6 +196,9 @@ __export_syshandler: callq __syshandler +.globl asm_restore_state; +.type asm_restore_state, @function; +asm_restore_state: // thread_context may have changed, therefore we reload it into %rcx anew. load_thread_context_addr restore_fpstate diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go index d2ec4accb..8a2c5b9a2 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go @@ -112,6 +112,16 @@ const ( // that there is a postponed interrupt from the syshandler. // The sentry should never see this event. ThreadStateInterrupt + // ThreadStateContextRestore means that the thread is in the process of doing + // a context restore. + ThreadStateContextRestore + // ThreadStateAsleep means that this thread fell asleep because there was not + // enough contexts to process in the context queue. + ThreadStateAsleep + // ThreadStateInitializing is only set once at sysmsg thread creation time. It + // is used to tell the signal handler that the thread does not yet have a + // context. + ThreadStateInitializing ) // Msg contains the current state of the sysmsg thread. @@ -162,9 +172,6 @@ type Msg struct { // fpState is an offset relative to the sighandler stack to the fpState, stored // by the sighandler. fpState uint64 - // TLS is a pointer to a thread local storage. - // It is is only populated on ARM64. - TLS uint64 // The fast path is the mode when a thread is polling msg->state to // wait for a required state instead of calling FUTEX_WAIT. // @@ -227,7 +234,7 @@ const ( const ( // MaxFPStateLen is the largest possible FPState that we will save. // Note: This value was chosen to be able to fit ThreadContext into one page. - MaxFPStateLen uint32 = 3648 + MaxFPStateLen uint32 = 3584 // AllocatedSizeofThreadContextStruct defines how much memory to allocate for // one instance of ThreadContext. @@ -265,6 +272,21 @@ type ThreadContext struct { // ThreadID is the ID of the sysmsg thread that's currently working on the // context. ThreadID uint32 + // LastThreadID is the ID of the previous sysmsg thread that ran the context + // (not the one currently working on it). This field is used by sysmsg threads + // to detect whether fpstate may have changed since the last time they ran a + // context. + LastThreadID uint32 + // SentryFastPath is used to indicate to the stub thread that the sentry + // goroutine used for this thread context is busy-polling for a response + // instead of using FUTEX_WAIT. + SentryFastPath uint32 + // Acked is used by sysmsg threads to signal to the sentry that this context + // has been picked up from the context queue and is actively being worked on. + Acked uint32 + // TLS is a pointer to a thread local storage. + // It is is only populated on ARM64. + TLS uint64 // Debug is a variable to use to get visibility into the stub from the sentry. Debug uint64 } @@ -277,6 +299,7 @@ func (m *Msg) Init(threadID uint32) { m.Line = -1 m.stubFastPath = 0 m.sentryFastPath = 1 + m.ThreadID = threadID } // Init initializes the ThreadContext instance. @@ -301,15 +324,35 @@ func (m *Msg) DisableStubFastPath() { // EnableSentryFastPath enables the polling mode for the Sentry. It has to be // called before switching controls to the stub process. +// This function is used if contextDecouplingExp=false because the fastpath +// is negotiated in Sysmsg. func (m *Msg) EnableSentryFastPath() { m.sentryFastPath = 1 } // DisableSentryFastPath disables the polling mode for the Sentry. +// This function is used if contextDecouplingExp=false because the fastpath +// is negotiated in Sysmsg. func (m *Msg) DisableSentryFastPath() { atomic.StoreUint32(&m.sentryFastPath, 0) } +// EnableSentryFastPath indicates that the polling mode is enabled for the +// Sentry. It has to be called before putting the context into the context queue. +// This function is used if contextDecouplingExp=true because the fastpath +// is negotiated in ThreadContext +func (c *ThreadContext) EnableSentryFastPath() { + c.SentryFastPath = 1 +} + +// DisableSentryFastPath indicates that the polling mode for the sentry is +// disabled for the Sentry. +// This function is used if contextDecouplingExp=true because the fastpath +// is negotiated in ThreadContext. +func (c *ThreadContext) DisableSentryFastPath() { + atomic.StoreUint32(&c.SentryFastPath, 0) +} + // FPUStateOffset returns the offset of a saved FPU state to the msg. func (m *Msg) FPUStateOffset() (uint64, error) { offset := m.fpState diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h index 3dd62bbd9..445d2cbbc 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h @@ -37,12 +37,15 @@ struct arch_state { #endif // LINT.IfChange -enum { +enum thread_state { THREAD_STATE_NONE, THREAD_STATE_DONE, THREAD_STATE_EVENT, THREAD_STATE_PREP, THREAD_STATE_INTERRUPT, + THREAD_STATE_CONTEXT_RESTORE, + THREAD_STATE_ASLEEP, + THREAD_STATE_INITIALIZING, }; // sysmsg contains the current state of the sysmsg thread. See: sysmsg.go:Msg @@ -65,8 +68,6 @@ struct sysmsg { int32_t err_line; uint64_t debug; uint64_t fpstate; - // tls is only populated on ARM64. - uint64_t tls; uint32_t stub_fast_path; uint32_t sentry_fast_path; uint32_t acked_events; @@ -96,6 +97,10 @@ struct thread_context { uint32_t state; uint32_t interrupt; uint32_t thread_id; + uint32_t last_thread_id; + uint32_t sentry_fast_path; + uint32_t acked; + uint64_t tls; uint64_t debug; }; @@ -118,6 +123,7 @@ extern uint64_t __export_pr_sched_core; extern uint64_t __export_deep_sleep_timeout; extern struct arch_state __export_arch_state; extern uint64_t __export_context_decoupling_exp; +extern uint64_t __export_context_queue_addr; // NOLINTBEGIN(runtime/int) static void *sysmsg_sp() { @@ -151,10 +157,15 @@ long sys_futex(uint32_t *addr, int op, int val, struct __kernel_timespec *tv, static void __panic(int err, long line) { void *sp = sysmsg_sp(); struct sysmsg *sysmsg = sysmsg_addr(sp); + struct thread_context *ctx = thread_context_addr(sysmsg); sysmsg->err = err; sysmsg->err_line = line; + // Normally sentry waits on sysmsg->state. __atomic_store_n(&sysmsg->state, THREAD_STATE_EVENT, __ATOMIC_RELEASE); sys_futex(&sysmsg->state, FUTEX_WAKE, 1, NULL, NULL, 666); + // Under context-decoupling the sentry waits on ctx->state. + __atomic_store_n(&ctx->state, CONTEXT_STATE_FAULT, __ATOMIC_RELEASE); + sys_futex(&ctx->state, FUTEX_WAKE, 1, NULL, NULL, 666); // crash the stub process. // // Normal user processes cannot map addresses lower than vm.mmap_min_addr @@ -165,7 +176,16 @@ static void __panic(int err, long line) { void memcpy(uint8_t *dest, uint8_t *src, size_t n); -int wait_state(struct sysmsg *sysmsg, uint32_t state); +void __export_start(struct sysmsg *sysmsg, void *_ucontext); + +void restore_state(struct sysmsg *sysmsg, struct thread_context *ctx, + void *_ucontext); + +struct thread_context *switch_context(struct sysmsg *sysmsg, + struct thread_context *ctx, + enum context_state new_context_state); + +int wait_state(struct sysmsg *sysmsg, enum thread_state new_thread_state); #define panic(err) __panic(err, __LINE__) // NOLINTEND(runtime/int) diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c index 358f3856f..265e2a2d8 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c @@ -27,25 +27,36 @@ // polling and fall asleep. uint64_t __export_deep_sleep_timeout; uint64_t __export_handshake_timeout; +uint64_t __export_context_queue_addr; -// A per-thread memory region is always align to STACK_SIZE. -// *------------* -// | guard page | -// |------------| -// | syshandler | -// | stack | -// | | -// |------------| -// | guard page | -// |------------| -// | | -// | ^ | -// | / \ | -// | | | -// | altstack | -// |------------| -// | sysmsg | -// *------------* +// LINT.IfChange +#define MAX_STUB_THREADS (4096) +#define MAX_CONTEXT_QUEUE_ENTRIES (MAX_STUB_THREADS + 1) +#define INVALID_CONTEXT_ID (MAX_STUB_THREADS + 1) +#define INVALID_THREAD_ID (MAX_STUB_THREADS + 1) + +// See systrap/context_queue.go +struct context_queue { + uint32_t start; + uint32_t end; + uint32_t polling_index; + uint32_t polling_index_base; + uint32_t num_sleeping_threads; + uint32_t ringbuffer[MAX_CONTEXT_QUEUE_ENTRIES]; +}; +// LINT.ThenChange(../context_queue.go) + +uint32_t is_empty(struct context_queue *queue) { + return __atomic_load_n(&queue->start, __ATOMIC_ACQUIRE) == + __atomic_load_n(&queue->end, __ATOMIC_ACQUIRE); +} + +int32_t queued_contexts(struct context_queue *queue) { + return (__atomic_load_n(&queue->end, __ATOMIC_ACQUIRE) + + MAX_CONTEXT_QUEUE_ENTRIES - + __atomic_load_n(&queue->start, __ATOMIC_ACQUIRE)) % + MAX_CONTEXT_QUEUE_ENTRIES; +} #if defined(__x86_64__) static __inline__ unsigned long rdtsc(void) { @@ -71,7 +82,92 @@ void memcpy(uint8_t *dest, uint8_t *src, size_t n) { } } -int wait_state(struct sysmsg *sysmsg, uint32_t state) { +// get_context retrieves a context that is ready to be restored to the user. +// This populates sysmsg->thread_context_id. +struct thread_context *get_context(struct sysmsg *sysmsg) { + struct context_queue *queue = + (struct context_queue *)(__export_context_queue_addr); + for (;;) { + // Change sysmsg thread state just to indicate thread is not asleep. + __atomic_store_n(&sysmsg->state, THREAD_STATE_PREP, __ATOMIC_RELEASE); + unsigned long start = rdtsc(); + for (;;) { + if (!is_empty(queue)) { + uint32_t next = __atomic_load_n(&queue->start, __ATOMIC_ACQUIRE) % + MAX_CONTEXT_QUEUE_ENTRIES; + uint32_t context_id = __atomic_exchange_n( + &queue->ringbuffer[next], INVALID_CONTEXT_ID, __ATOMIC_ACQ_REL); + if (context_id != INVALID_CONTEXT_ID) { + __atomic_add_fetch(&queue->start, 1, __ATOMIC_ACQ_REL); + if (context_id > MAX_STUB_THREADS) { + panic(context_id); + } + sysmsg->context_id = context_id; + struct thread_context *ctx = thread_context_addr(sysmsg); + __atomic_store_n(&ctx->acked, 1, __ATOMIC_RELEASE); + __atomic_store_n(&ctx->thread_id, sysmsg->thread_id, + __ATOMIC_RELEASE); + return ctx; + } else { + continue; + } + } + if ((rdtsc() - start) > __export_deep_sleep_timeout) { + break; + } + + spinloop(); + } + __atomic_store_n(&sysmsg->state, THREAD_STATE_ASLEEP, __ATOMIC_RELEASE); + + __atomic_add_fetch(&queue->num_sleeping_threads, 1, __ATOMIC_ACQ_REL); + sys_futex(&sysmsg->state, FUTEX_WAIT, THREAD_STATE_ASLEEP, NULL, NULL, 0); + __atomic_sub_fetch(&queue->num_sleeping_threads, 1, __ATOMIC_ACQ_REL); + } +} + +// switch_context signals the sentry that the old context is ready to be worked +// on and retrieves a new context to switch to. +struct thread_context *switch_context(struct sysmsg *sysmsg, + struct thread_context *ctx, + enum context_state new_context_state) { + __atomic_store_n(&ctx->thread_id, INVALID_THREAD_ID, __ATOMIC_RELEASE); + __atomic_store_n(&ctx->last_thread_id, sysmsg->thread_id, __ATOMIC_RELEASE); + __atomic_store_n(&ctx->state, new_context_state, __ATOMIC_RELEASE); + if (__atomic_load_n(&ctx->sentry_fast_path, __ATOMIC_ACQUIRE) == 0) { + int ret = sys_futex(&ctx->state, FUTEX_WAKE, 1, NULL, NULL, 0); + if (ret < 0) { + panic(ret); + } + } + uint32_t old_ctx_id = sysmsg->context_id; + + ctx = get_context(sysmsg); + + if (old_ctx_id != sysmsg->context_id || + ctx->last_thread_id != sysmsg->thread_id) { + ctx->fpstate_changed = 1; + } + + return ctx; +} + +void __export_start(struct sysmsg *sysmsg, void *_ucontext) { +#if defined(__x86_64__) + asm volatile("movq %%gs:0, %0\n" : "=r"(sysmsg) : :); + if (sysmsg->self != sysmsg) { + panic(0xdeaddead); + } +#endif + + struct thread_context *ctx = get_context(sysmsg); + __atomic_store_n(&ctx->fpstate_changed, 1, __ATOMIC_RELEASE); + __atomic_store_n(&ctx->thread_id, sysmsg->thread_id, __ATOMIC_RELEASE); + + restore_state(sysmsg, ctx, _ucontext); +} + +int wait_state(struct sysmsg *sysmsg, enum thread_state new_thread_state) { unsigned long handshake_timeout; uint64_t acked_events_prev; unsigned long start; @@ -81,7 +177,7 @@ int wait_state(struct sysmsg *sysmsg, uint32_t state) { // stub_fast_path can be changed non-atomically before we change the state and // wake up the Sentry. sysmsg->stub_fast_path = 1; - __atomic_store_n(&sysmsg->state, state, __ATOMIC_SEQ_CST); + __atomic_store_n(&sysmsg->state, new_thread_state, __ATOMIC_SEQ_CST); fast_path = __atomic_load_n(&sysmsg->sentry_fast_path, __ATOMIC_SEQ_CST); if (!fast_path) { diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h index 0c0ec5514..7d6862393 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h @@ -21,7 +21,7 @@ #define FAULT_OPCODE 0x06 // LINT.IfChange -#define MAX_FPSTATE_LEN 3648 +#define MAX_FPSTATE_LEN 3584 // Note: To be explicit, 2^12 = 4096; if ALLOCATED_SIZEOF_THREAD_CONTEXT_STRUCT // is changed, make sure to change the code that relies on the bitshift. #define ALLOCATED_SIZEOF_THREAD_CONTEXT_STRUCT 4096 diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go new file mode 100644 index 000000000..3f762e116 --- /dev/null +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go @@ -0,0 +1,40 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sysmsg + +import ( + "fmt" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/abi/linux" +) + +// SleepOnState makes the caller sleep on the ThreadContext.State futex. +func (c *ThreadContext) SleepOnState(curState ContextState) { + _, _, errno := unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&c.State)), + linux.FUTEX_WAIT, uintptr(curState), 0, 0, 0) + if errno != 0 && errno != unix.EAGAIN && errno != unix.EINTR { + panic(fmt.Sprintf("error waiting for state: %v", errno)) + } +} + +// WakeSysmsgThread calls futex wake on Sysmsg.State. +func (m *Msg) WakeSysmsgThread() syscall.Errno { + m.State.Set(ThreadStatePrep) + _, _, e := unix.RawSyscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&m.State)), linux.FUTEX_WAKE, 1, 0, 0, 0) + return e +} diff --git a/pkg/sentry/platform/systrap/sysmsg_thread.go b/pkg/sentry/platform/systrap/sysmsg_thread.go index c5a133404..ddcd105e5 100644 --- a/pkg/sentry/platform/systrap/sysmsg_thread.go +++ b/pkg/sentry/platform/systrap/sysmsg_thread.go @@ -47,10 +47,6 @@ type sysmsgThread struct { // context is the last context that ran on this thread. context *context - // gsBase contains previous values of gs_base register to follow - // changes, because it's not restored by the kernel from a signal frame. - gsBase uint64 - // stackRange is a sysmsg stack in the memory file. stackRange memmap.FileRange diff --git a/pkg/sentry/platform/systrap/systrap.go b/pkg/sentry/platform/systrap/systrap.go index 4fa37ec89..64e919bd0 100644 --- a/pkg/sentry/platform/systrap/systrap.go +++ b/pkg/sentry/platform/systrap/systrap.go @@ -80,6 +80,9 @@ var ( stubSysmsgStack uintptr stubSysmsgStart uintptr stubSysmsgEnd uintptr + // Memory region to store the contextQueue. + stubContextQueueRegion uintptr + stubContextQueueRegionLen uintptr // Memory region to store instances of sysmsg.ThreadContext. stubContextRegion uintptr stubContextRegionLen uintptr @@ -131,7 +134,7 @@ type context struct { lastFaultIP hostarch.Addr // sysmsgThread is a sysmsg thread descriptor which is used to execute - // application code. + // application code. (Note: Unused if contextDecouplingExp=true). sysmsgThread *sysmsgThread // fpLen is the size of the floating point context. @@ -281,10 +284,10 @@ func (c *context) Interrupt() { // NotifyInterrupt implements interrupt.Receiver.NotifyInterrupt. // // Another reasonable existing object to implement NotifyInterrupt would be -// sysmsg.ThreadContext, because it already has the correct tid written into it -// to know which thread to send the signal to. However we cannot do that because -// it is in shared memory, which means that one subprocess can overwrite it to -// have the sentry send an interrupt to a completely different subprocess. +// sysmsg.ThreadContext, because we can write the correct host TID into it +// to know which thread to send the signal to. However, because it is in shared +// memory, one subprocess can overwrite it to have the sentry send an interrupt +// to a completely different subprocess. // For this reason we use systrap.context and check that the target thread // is actually valid within the subprocess. func (c *context) NotifyInterrupt() { From 9abcf28e601480db39ef99483c747ca6277e2d42 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Tue, 14 Mar 2023 21:48:37 -0700 Subject: [PATCH 09/49] Schedule GRO flush iff packets are queued Originally Ghanan's cl/494241863. This prevents goroutines from being unnecessarily scheduled when there are no packets in the GRO dispatcher, reducing wasted CPU cycles. With this change, a timer to flush GRO packets is scheduled when a packet is enqueued and no previous timer exists. The timer is rescheduled to be fired again if after flushing packets, packets remain in the GRO dispatcher. If GRO is disabled, and a timer was previously set, the timer is reset to fire immediately and flush all packets. Performance: The big win here is for non-network-heavy workloads. The GRO flush thread no longer fires when unnecessary. GRO used to add absurd overhead for such workloads. Now it adds none: e.g. `sleep 10` used <= 10ms without GRO, 7.79s with it (!!!), and now uses <= 10ms with this change. This lowers throughput and CPU usage roughly proportionally in networking-heavy benchmarking. CPU usage seems to decrease slightly more than throughput (which is good!). PiperOrigin-RevId: 516720406 --- pkg/tcpip/stack/gro.go | 126 ++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/pkg/tcpip/stack/gro.go b/pkg/tcpip/stack/gro.go index b403c05d4..a3b708fd5 100644 --- a/pkg/tcpip/stack/gro.go +++ b/pkg/tcpip/stack/gro.go @@ -24,6 +24,7 @@ import ( "gvisor.dev/gvisor/pkg/tcpip/header" ) +// TODO(b/256037250): Enable by default. // TODO(b/256037250): We parse headers here. We should save those headers in // PacketBuffers so they don't have to be re-parsed later. // TODO(b/256037250): I still see the occasional SACK block in the zero-loss @@ -33,7 +34,6 @@ import ( // opportunity for coalescing. // TODO(b/256037250): We're doing some header parsing here, which presents the // opportunity to skip it later. -// TODO(b/256037250): Disarm or ignore the timer when GRO is empty. // TODO(b/256037250): We may be able to remove locking by pairing // groDispatchers with link endpoint dispatchers. @@ -222,7 +222,7 @@ func (gb *groBucket) findGROPacket6(pkt PacketBufferPtr, ipHdr header.IPv6, tcpH } // +checklocks:gb.mu -func (gb *groBucket) found(groPkt *groPacket, flushGROPkt bool, pkt PacketBufferPtr, ipHdr []byte, tcpHdr header.TCP, ep NetworkEndpoint, updateIPHdr func([]byte, int)) { +func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt bool, pkt PacketBufferPtr, ipHdr []byte, tcpHdr header.TCP, ep NetworkEndpoint, updateIPHdr func([]byte, int)) { // Flush groPkt or merge the packets. pktSize := pkt.Data().Size() flags := tcpHdr.Flags() @@ -294,6 +294,11 @@ func (gb *groBucket) found(groPkt *groPacket, flushGROPkt bool, pkt PacketBuffer // A merge occurred and we don't need to flush anything. gb.mu.Unlock() } + + // Schedule a timer if we never had one set before. + if gd.flushTimerState.CompareAndSwap(flushTimerUnset, flushTimerSet) { + gd.flushTimer.Reset(gd.getInterval()) + } } // A groPacket is packet undergoing GRO. It may be several packets coalesced @@ -341,23 +346,26 @@ func (pk *groPacket) payloadSize() int { return pk.pkt.Data().Size() - len(pk.ipHdr) - int(pk.tcpHdr.DataOffset()) } +// Values held in groDispatcher.flushTimerState. +const ( + flushTimerUnset = iota + flushTimerSet + flushTimerClosed +) + // groDispatcher coalesces incoming packets to increase throughput. type groDispatcher struct { - // newInterval notifies about changes to the interval. - newInterval chan struct{} // intervalNS is the interval in nanoseconds. intervalNS atomicbitops.Int64 - // stop instructs the GRO dispatcher goroutine to stop. - stop chan struct{} buckets [groNBuckets]groBucket - wg sync.WaitGroup + + flushTimerState atomicbitops.Int32 + flushTimer *time.Timer } func (gd *groDispatcher) init(interval time.Duration) { gd.intervalNS.Store(interval.Nanoseconds()) - gd.newInterval = make(chan struct{}, 1) - gd.stop = make(chan struct{}) for i := range gd.buckets { bucket := &gd.buckets[i] @@ -369,59 +377,49 @@ func (gd *groDispatcher) init(interval time.Duration) { bucket.mu.Unlock() } - gd.start(interval) -} + // Create a timer to fire far from now and cancel it immediately. + // + // The timer will be reset when there is a need for it to fire. + gd.flushTimer = time.AfterFunc(time.Hour, func() { + if !gd.flushTimerState.CompareAndSwap(flushTimerSet, flushTimerUnset) { + // Timer was unset or GRO is closed, do nothing further. + return + } -// start spawns a goroutine that flushes the GRO periodically based on the -// interval. -func (gd *groDispatcher) start(interval time.Duration) { - gd.wg.Add(1) - - go func(interval time.Duration) { - defer gd.wg.Done() - - var ch <-chan time.Time + interval := gd.getInterval() if interval == 0 { - // Never run. - ch = make(<-chan time.Time) - } else { - ticker := time.NewTicker(interval) - ch = ticker.C + gd.flushAll() + return } - for { - select { - case <-gd.newInterval: - interval = time.Duration(gd.intervalNS.Load()) * time.Nanosecond - if interval == 0 { - // Never run. Flush any existing GRO packets. - gd.flushAll() - ch = make(<-chan time.Time) - } else { - ticker := time.NewTicker(interval) - ch = ticker.C - } - case <-ch: - gd.flush() - case <-gd.stop: - return - } + + if gd.flush() && gd.flushTimerState.CompareAndSwap(flushTimerUnset, flushTimerSet) { + // Only reset the timer if we have more packets and the timer was + // previously unset. If we have no packets left, the timer is already set + // or GRO is being closed, do not reset the timer. + gd.flushTimer.Reset(interval) } - }(interval) + }) + gd.flushTimer.Stop() } func (gd *groDispatcher) getInterval() time.Duration { return time.Duration(gd.intervalNS.Load()) * time.Nanosecond } +// setInterval is not thread-safe and so much be protected by callers. func (gd *groDispatcher) setInterval(interval time.Duration) { gd.intervalNS.Store(interval.Nanoseconds()) - gd.newInterval <- struct{}{} + + if gd.flushTimerState.Load() == flushTimerSet { + // Timer was previously set, reset it. + gd.flushTimer.Reset(interval) + } } // dispatch sends pkt up the stack after it undergoes GRO coalescing. func (gd *groDispatcher) dispatch(pkt PacketBufferPtr, netProto tcpip.NetworkProtocolNumber, ep NetworkEndpoint) { // If GRO is disabled simply pass the packet along. - if gd.intervalNS.Load() == 0 { + if gd.getInterval() == 0 { ep.HandlePacket(pkt) return } @@ -503,7 +501,7 @@ func (gd *groDispatcher) dispatch4(pkt PacketBufferPtr, ep NetworkEndpoint) { bucket := &gd.buckets[gd.bucketForPacket(ipHdr, tcpHdr)&groNBucketsMask] bucket.mu.Lock() groPkt, flushGROPkt := bucket.findGROPacket4(pkt, ipHdr, tcpHdr, ep) - bucket.found(groPkt, flushGROPkt, pkt, ipHdr, tcpHdr, ep, updateIPv4Hdr) + bucket.found(gd, groPkt, flushGROPkt, pkt, ipHdr, tcpHdr, ep, updateIPv4Hdr) } func (gd *groDispatcher) dispatch6(pkt PacketBufferPtr, ep NetworkEndpoint) { @@ -601,7 +599,7 @@ func (gd *groDispatcher) dispatch6(pkt PacketBufferPtr, ep NetworkEndpoint) { bucket := &gd.buckets[gd.bucketForPacket(ipHdr, tcpHdr)&groNBucketsMask] bucket.mu.Lock() groPkt, flushGROPkt := bucket.findGROPacket6(pkt, ipHdr, tcpHdr, ep) - bucket.found(groPkt, flushGROPkt, pkt, ipHdr, tcpHdr, ep, updateIPv6Hdr) + bucket.found(gd, groPkt, flushGROPkt, pkt, ipHdr, tcpHdr, ep, updateIPv6Hdr) } func (gd *groDispatcher) bucketForPacket(ipHdr header.Network, tcpHdr header.TCP) int { @@ -620,18 +618,26 @@ func (gd *groDispatcher) bucketForPacket(ipHdr header.Network, tcpHdr header.TCP } // flush sends any packets older than interval up the stack. -func (gd *groDispatcher) flush() { +// +// Returns true iff packets remain. +func (gd *groDispatcher) flush() bool { interval := gd.intervalNS.Load() old := time.Now().Add(-time.Duration(interval) * time.Nanosecond) - gd.flushSince(old) + return gd.flushSinceOrEqualTo(old) } -func (gd *groDispatcher) flushSince(old time.Time) { +// flushSinceOrEqualTo sends any packets older than or equal to the specified +// time. +// +// Returns true iff packets remain. +func (gd *groDispatcher) flushSinceOrEqualTo(old time.Time) bool { type pair struct { pkt PacketBufferPtr ep NetworkEndpoint } + hasMore := false + for i := range gd.buckets { // Put packets in a slice so we don't have to hold bucket.mu // when we call HandlePacket. @@ -641,13 +647,14 @@ func (gd *groDispatcher) flushSince(old time.Time) { bucket := &gd.buckets[i] bucket.mu.Lock() for groPkt := bucket.packets.Front(); groPkt != nil; groPkt = groPkt.Next() { - if groPkt.created.Before(old) { - pairs = append(pairs, pair{groPkt.pkt, groPkt.ep}) - bucket.removeOne(groPkt) - } else { + if groPkt.created.After(old) { // Packets are ordered by age, so we can move // on once we find one that's too new. + hasMore = true break + } else { + pairs = append(pairs, pair{groPkt.pkt, groPkt.ep}) + bucket.removeOne(groPkt) } } bucket.mu.Unlock() @@ -657,16 +664,21 @@ func (gd *groDispatcher) flushSince(old time.Time) { pair.pkt.DecRef() } } + + return hasMore } func (gd *groDispatcher) flushAll() { - gd.flushSince(time.Now()) + if gd.flushSinceOrEqualTo(time.Now()) { + panic("packets unexpectedly remain after flushing all") + } } // close stops the GRO goroutine and releases any held packets. func (gd *groDispatcher) close() { - gd.stop <- struct{}{} - gd.wg.Wait() + gd.flushTimer.Stop() + // Prevent the timer from being scheduled again. + gd.flushTimerState.Store(flushTimerClosed) for i := range gd.buckets { bucket := &gd.buckets[i] From 3653ddc1c9f2ac7d487e975b4cde5057b241d7bf Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Wed, 15 Mar 2023 09:24:57 -0700 Subject: [PATCH 10/49] Internal change. PiperOrigin-RevId: 516846082 --- test/util/logging.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/util/logging.h b/test/util/logging.h index 5c17f1233..5f3cc46cb 100644 --- a/test/util/logging.h +++ b/test/util/logging.h @@ -17,6 +17,8 @@ #include +#include + namespace gvisor { namespace testing { From cc1f80913f72a39214315f2d13f76295a5a324d8 Mon Sep 17 00:00:00 2001 From: Nick Brown Date: Wed, 15 Mar 2023 10:25:33 -0700 Subject: [PATCH 11/49] Defer poll timeout to Fuchsia's CI infra Set the ICMP poll timeout to a negative value so the poll will block indefinitely. This effectively delegates the timeout to the overall test timeout imposed by Fuchsia's CI/CQ. PiperOrigin-RevId: 516863038 --- test/syscalls/linux/udp_socket.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc index da8dafe53..345301049 100644 --- a/test/syscalls/linux/udp_socket.cc +++ b/test/syscalls/linux/udp_socket.cc @@ -53,10 +53,11 @@ namespace testing { namespace { -size_t IcmpTimeoutMillis() { - // Fuchsia's CI infra is susceptible to timing jumps. Set a long timeout - // to avoid flakes. - return GvisorPlatform() == Platform::kFuchsia ? 100000 : 1000; +int IcmpTimeoutMillis() { + // Fuchsia's CI infra is susceptible to timing jumps. Set a negative timeout + // so that poll will block indefinitely, which effectively delegates the + // timeout to infra. + return GvisorPlatform() == Platform::kFuchsia ? -1 : 1000; } // Fixture for tests parameterized by the address family to use (AF_INET and From c122d8d6c82da43a6de4879a5f08365c6ba3c85f Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Wed, 15 Mar 2023 10:52:04 -0700 Subject: [PATCH 12/49] gro: fix bug where handshake packets would be stuck waiting in GRO Also enables GRO in syscall tests. GRO should be totally transparent and should not affect behavior. This ensures it's tested and fixes the bug it uncovered. GRO would not immediately flush the final ACK in the SYN-SYN/ACK-ACK handshake. This could lead to a situation where - Client A calls connect(), which returns once the final ACK of the handshake is sent and A reaches state ESTABLISHED - The ACK gets GRO'd, delaying it from reaching the server - Client B calls non-blocking connect() - Client B's ACK gets GRO'd as well - Client B is marked as ESTABLISHED - The server, with accept queue size 1, is only going to accept one connection, but two clients are ESTABLISHED. It now immediately flushes packets with no payload, as they are important to TCP connection state and management. PiperOrigin-RevId: 516870932 --- pkg/tcpip/stack/gro.go | 5 +++-- runsc/boot/network.go | 20 +++++++++++++------- runsc/sandbox/network.go | 16 ++++++++++------ test/runner/main.go | 1 + 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/pkg/tcpip/stack/gro.go b/pkg/tcpip/stack/gro.go index a3b708fd5..29a1b7061 100644 --- a/pkg/tcpip/stack/gro.go +++ b/pkg/tcpip/stack/gro.go @@ -226,6 +226,8 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo // Flush groPkt or merge the packets. pktSize := pkt.Data().Size() flags := tcpHdr.Flags() + dataOff := tcpHdr.DataOffset() + tcpPayloadSize := pkt.Data().Size() - len(ipHdr) - int(dataOff) if flushGROPkt { // Flush the existing GRO packet. Don't hold bucket.mu while // processing the packet. @@ -239,12 +241,10 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo } else if groPkt != nil { // Merge pkt in to GRO packet. buf := pkt.Data().ToBuffer() - dataOff := tcpHdr.DataOffset() buf.TrimFront(int64(len(ipHdr)) + int64(dataOff)) groPkt.pkt.Data().MergeBuffer(&buf) buf.Release() // Update the IP total length. - tcpPayloadSize := pkt.Data().Size() - len(ipHdr) - int(dataOff) updateIPHdr(groPkt.ipHdr, tcpPayloadSize) // Add flags from the packet to the GRO packet. groPkt.tcpHdr.SetFlags(uint8(groPkt.tcpHdr.Flags() | (flags & (header.TCPFlagFin | header.TCPFlagPsh)))) @@ -261,6 +261,7 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo // malformed, a local GSO packet, or has already been handled by host // GRO. flush := header.TCPFlags(flags)&(header.TCPFlagUrg|header.TCPFlagPsh|header.TCPFlagRst|header.TCPFlagSyn|header.TCPFlagFin) != 0 + flush = flush || tcpPayloadSize == 0 if groPkt != nil { flush = flush || pktSize != groPkt.initialLength } diff --git a/runsc/boot/network.go b/runsc/boot/network.go index b7b34c630..4f9c2d112 100644 --- a/runsc/boot/network.go +++ b/runsc/boot/network.go @@ -121,17 +121,19 @@ type XDPLink struct { LinkAddress net.HardwareAddr QDisc config.QueueingDiscipline Neighbors []Neighbor + GvisorGROTimeout time.Duration // NumChannels controls how many underlying FDs are to be used to // create this endpoint. NumChannels int } -// LoopbackLink configures a loopback li nk. +// LoopbackLink configures a loopback link. type LoopbackLink struct { - Name string - Addresses []IPWithPrefix - Routes []Route + Name string + Addresses []IPWithPrefix + Routes []Route + GvisorGROTimeout time.Duration } // CreateLinksAndRoutesArgs are arguments to CreateLinkAndRoutes. @@ -216,7 +218,10 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct linkEP := packetsocket.New(ethernet.New(loopback.New())) log.Infof("Enabling loopback interface %q with id %d on addresses %+v", link.Name, nicID, link.Addresses) - opts := stack.NICOptions{Name: link.Name} + opts := stack.NICOptions{ + Name: link.Name, + GROTimeout: link.GvisorGROTimeout, + } if err := n.createNICWithAddrs(nicID, linkEP, opts, link.Addresses); err != nil { return err } @@ -383,8 +388,9 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct log.Infof("Enabling interface %q with id %d on addresses %+v (%v) w/ %d channels", link.Name, nicID, link.Addresses, mac, link.NumChannels) opts := stack.NICOptions{ - Name: link.Name, - QDisc: qDisc, + Name: link.Name, + QDisc: qDisc, + GROTimeout: link.GvisorGROTimeout, } if err := n.createNICWithAddrs(nicID, sniffEP, opts, link.Addresses); err != nil { return err diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index f5b1a00b3..5a9773f19 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -60,7 +60,7 @@ func setupNetwork(conn *urpc.Client, pid int, conf *config.Config) error { switch conf.Network { case config.NetworkNone: log.Infof("Network is disabled, create loopback interface only") - if err := createDefaultLoopbackInterface(conn); err != nil { + if err := createDefaultLoopbackInterface(conf, conn); err != nil { return fmt.Errorf("creating default loopback interface: %v", err) } case config.NetworkSandbox: @@ -78,9 +78,11 @@ func setupNetwork(conn *urpc.Client, pid int, conf *config.Config) error { return nil } -func createDefaultLoopbackInterface(conn *urpc.Client) error { +func createDefaultLoopbackInterface(conf *config.Config, conn *urpc.Client) error { + link := boot.DefaultLoopbackLink + link.GvisorGROTimeout = conf.GvisorGROTimeout if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &boot.CreateLinksAndRoutesArgs{ - LoopbackLinks: []boot.LoopbackLink{boot.DefaultLoopbackLink}, + LoopbackLinks: []boot.LoopbackLink{link}, }, nil); err != nil { return fmt.Errorf("creating loopback link and routes: %v", err) } @@ -157,7 +159,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con // We build our own loopback device. if iface.Flags&net.FlagLoopback != 0 { - link, err := loopbackLink(iface, allAddrs) + link, err := loopbackLink(conf, iface, allAddrs) if err != nil { return fmt.Errorf("getting loopback link for iface %q: %w", iface.Name, err) } @@ -261,6 +263,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con Neighbors: neighbors, LinkAddress: linkAddress, Addresses: addresses, + GvisorGROTimeout: conf.GvisorGROTimeout, }) } else { link := boot.FDBasedLink{ @@ -492,9 +495,10 @@ func createSocketXDP(iface net.Interface) ([]*os.File, error) { // loopbackLink returns the link with addresses and routes for a loopback // interface. -func loopbackLink(iface net.Interface, addrs []net.Addr) (boot.LoopbackLink, error) { +func loopbackLink(conf *config.Config, iface net.Interface, addrs []net.Addr) (boot.LoopbackLink, error) { link := boot.LoopbackLink{ - Name: iface.Name, + Name: iface.Name, + GvisorGROTimeout: conf.GvisorGROTimeout, } for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) diff --git a/test/runner/main.go b/test/runner/main.go index c017499dd..5ad4ceaf7 100644 --- a/test/runner/main.go +++ b/test/runner/main.go @@ -229,6 +229,7 @@ func runRunsc(tc *gtest.TestCase, spec *specs.Spec) error { "-watchdog-action=panic", "-platform", *platform, "-file-access", *fileAccess, + "-gvisor-gro=200000ns", } if *network == "host" && !testutil.TestEnvSupportsRawSockets { From 4c5803c47fbe3fe5893c515ffa6043c9576ccb86 Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Wed, 15 Mar 2023 12:17:58 -0700 Subject: [PATCH 13/49] Improve lisafs debug log messages. - Improves Statx prints. Used by Inode, which is in turn used by many messages. Now mode and timestamps are much more readable. - Print all mask fields as hex for readability. - Made WalkStatus readable. - Improves printing of slices. Earlier, there was a ", " suffix which was confusing because it could look like an empty string entry. Now we print slices more meticulously. - Consistently use Stringer implementations to print things, instead of %+v. So future improvements are easier to make. PiperOrigin-RevId: 516895609 --- pkg/abi/linux/file.go | 4 +-- pkg/abi/linux/time.go | 5 +++ pkg/lisafs/message.go | 77 ++++++++++++++++++++++++++++++++----------- 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/pkg/abi/linux/file.go b/pkg/abi/linux/file.go index 242608e3b..a56ff8ee5 100644 --- a/pkg/abi/linux/file.go +++ b/pkg/abi/linux/file.go @@ -274,8 +274,8 @@ type Statx struct { // String implements fmt.Stringer.String. func (s *Statx) String() string { - return fmt.Sprintf("Statx{Mask: %d, Blksize: %d, Attributes: %d, Nlink: %d, UID: %d, GID: %d, Mode: %d, Ino: %d, Size: %d, Blocks: %d, AttributesMask: %d, Atime: %d, Btime: %d, Ctime: %d, Mtime: %d, RdevMajor: %d, RdevMinor: %d, DevMajor: %d, DevMinor: %d}", - s.Mask, s.Blksize, s.Attributes, s.Nlink, s.UID, s.GID, s.Mode, s.Ino, s.Size, s.Blocks, s.AttributesMask, s.Atime, s.Btime, s.Ctime, s.Mtime, s.RdevMajor, s.RdevMinor, s.DevMajor, s.DevMinor) + return fmt.Sprintf("Statx{Mask: %#x, Mode: %s, UID: %d, GID: %d, Ino: %d, DevMajor: %d, DevMinor: %d, Size: %d, Blocks: %d, Blksize: %d, Nlink: %d, Atime: %s, Btime: %s, Ctime: %s, Mtime: %s, Attributes: %d, AttributesMask: %d, RdevMajor: %d, RdevMinor: %d}", + s.Mask, FileMode(s.Mode), s.UID, s.GID, s.Ino, s.DevMajor, s.DevMinor, s.Size, s.Blocks, s.Blksize, s.Nlink, s.Atime.ToTime(), s.Btime.ToTime(), s.Ctime.ToTime(), s.Mtime.ToTime(), s.Attributes, s.AttributesMask, s.RdevMajor, s.RdevMinor) } // SizeOfStatx is the size of a Statx struct. diff --git a/pkg/abi/linux/time.go b/pkg/abi/linux/time.go index 45a739b24..09407b704 100644 --- a/pkg/abi/linux/time.go +++ b/pkg/abi/linux/time.go @@ -274,6 +274,11 @@ func NsecToStatxTimestamp(nsec int64) (ts StatxTimestamp) { } } +// ToTime returns the Go time.Time representation. +func (sxts StatxTimestamp) ToTime() time.Time { + return time.Unix(sxts.Sec, int64(sxts.Nsec)) +} + // Utime represents struct utimbuf used by utimes(2). // // +marshal diff --git a/pkg/lisafs/message.go b/pkg/lisafs/message.go index 94aa3c657..786d388f7 100644 --- a/pkg/lisafs/message.go +++ b/pkg/lisafs/message.go @@ -270,9 +270,7 @@ type StringArray []string func (s *StringArray) String() string { var b strings.Builder b.WriteString("[") - for _, str := range *s { - b.WriteString(fmt.Sprintf("%s, ", str)) - } + b.WriteString(strings.Join(*s, ", ")) b.WriteString("]") return b.String() } @@ -331,6 +329,10 @@ type Inode struct { Stat linux.Statx } +func (i *Inode) String() string { + return fmt.Sprintf("Inode{ControlFD: %d, Stat: %s}", i.ControlFD, i.Stat.String()) +} + // MountReq is an empty request to Mount on the connection. type MountReq struct{ EmptyMessage } @@ -351,7 +353,7 @@ type MountResp struct { // String implements fmt.Stringer.String. func (m *MountResp) String() string { - return fmt.Sprintf("MountResp{Root: %+v, MaxMessageSize: %d, SupportedMs: %+v}", m.Root, m.MaxMessageSize, m.SupportedMs) + return fmt.Sprintf("MountResp{Root: %s, MaxMessageSize: %d, SupportedMs: %+v}", m.Root.String(), m.MaxMessageSize, m.SupportedMs) } // SizeBytes implements marshal.Marshallable.SizeBytes. @@ -453,8 +455,8 @@ type SetStatReq struct { // String implements fmt.Stringer.String. func (s *SetStatReq) String() string { - return fmt.Sprintf("SetStatReq{FD: %d, Mask: %d, Mode: %d, UID: %d, GID: %d, Size: %d, Atime: %+v, Mtime: %+v}", - s.FD, s.Mask, s.Mode, s.UID, s.GID, s.Size, s.Atime, s.Mtime) + return fmt.Sprintf("SetStatReq{FD: %d, Mask: %#x, Mode: %d, UID: %d, GID: %d, Size: %d, Atime: %s, Mtime: %s}", + s.FD, s.Mask, s.Mode, s.UID, s.GID, s.Size, s.Atime.ToTime(), s.Mtime.ToTime()) } // SetStatResp is used to communicate SetStat results. It contains a mask @@ -470,7 +472,7 @@ type SetStatResp struct { // String implements fmt.Stringer.String. func (s *SetStatResp) String() string { - return fmt.Sprintf("SetStatResp{FailureMask: %d, FailureErrNo: %d}", s.FailureMask, s.FailureErrNo) + return fmt.Sprintf("SetStatResp{FailureMask: %#x, FailureErrNo: %d}", s.FailureMask, s.FailureErrNo) } // WalkReq is used to request to walk multiple path components at once. This @@ -530,6 +532,19 @@ const ( WalkComponentSymlink ) +func walkStatusToString(ws WalkStatus) string { + switch ws { + case WalkSuccess: + return "Success" + case WalkComponentDoesNotExist: + return "ComponentDoesNotExist" + case WalkComponentSymlink: + return "ComponentSymlink" + default: + panic(fmt.Sprintf("Unknown WalkStatus: %d", ws)) + } +} + // WalkResp is used to communicate the inodes walked by the server. In memory, // the inode array is preceded by a uint16 integer denoting array length. type WalkResp struct { @@ -544,10 +559,13 @@ func (w *WalkResp) String() string { var arrB strings.Builder arrB.WriteString("[") for i := range w.Inodes { - arrB.WriteString(fmt.Sprintf("%+v, ", w.Inodes[i])) + if i > 0 { + arrB.WriteString(", ") + } + arrB.WriteString(w.Inodes[i].String()) } arrB.WriteString("]") - return fmt.Sprintf("WalkResp{Status: %d, Inodes: %s}", w.Status, arrB.String()) + return fmt.Sprintf("WalkResp{Status: %s, Inodes: %s}", walkStatusToString(w.Status), arrB.String()) } // SizeBytes implements marshal.Marshallable.SizeBytes. @@ -595,7 +613,16 @@ type WalkStatResp struct { // String implements fmt.Stringer.String. func (w *WalkStatResp) String() string { - return fmt.Sprintf("WalkStatResp{Stats: %+v}", w.Stats) + var arrB strings.Builder + arrB.WriteString("[") + for i := range w.Stats { + if i > 0 { + arrB.WriteString(", ") + } + arrB.WriteString(w.Stats[i].String()) + } + arrB.WriteString("]") + return fmt.Sprintf("WalkStatResp{Stats: %s}", arrB.String()) } // SizeBytes implements marshal.Marshallable.SizeBytes. @@ -716,7 +743,7 @@ type OpenCreateAtResp struct { // String implements fmt.Stringer.String. func (o *OpenCreateAtResp) String() string { - return fmt.Sprintf("OpenCreateAtResp{Child: %+v, NewFD: %d}", o.Child, o.NewFD) + return fmt.Sprintf("OpenCreateAtResp{Child: %s, NewFD: %d}", o.Child.String(), o.NewFD) } // FdArray is a utility struct which implements a marshallable type for @@ -730,8 +757,11 @@ type FdArray []FDID func (f *FdArray) String() string { var b strings.Builder b.WriteString("[") - for _, fd := range *f { - b.WriteString(fmt.Sprintf("%d, ", fd)) + for i, fd := range *f { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(fmt.Sprintf("%d", fd)) } b.WriteString("]") return b.String() @@ -985,7 +1015,7 @@ type MkdirAtResp struct { // String implements fmt.Stringer.String. func (m *MkdirAtResp) String() string { - return fmt.Sprintf("MkdirAtResp{ChildDir: %+v}", m.ChildDir) + return fmt.Sprintf("MkdirAtResp{ChildDir: %s}", m.ChildDir.String()) } // MknodAtReq is used to make MknodAt requests. @@ -1038,7 +1068,7 @@ type MknodAtResp struct { // String implements fmt.Stringer.String. func (m *MknodAtResp) String() string { - return fmt.Sprintf("MknodAtResp{Child: %+v}", m.Child) + return fmt.Sprintf("MknodAtResp{Child: %s}", m.Child.String()) } // SymlinkAtReq is used to make SymlinkAt request. @@ -1098,7 +1128,7 @@ type SymlinkAtResp struct { // String implements fmt.Stringer.String. func (s *SymlinkAtResp) String() string { - return fmt.Sprintf("SymlinkAtResp{Symlink: %+v}", s.Symlink) + return fmt.Sprintf("SymlinkAtResp{Symlink: %s}", s.Symlink.String()) } // LinkAtReq is used to make LinkAt requests. @@ -1148,7 +1178,7 @@ type LinkAtResp struct { // String implements fmt.Stringer.String. func (l *LinkAtResp) String() string { - return fmt.Sprintf("LinkAtResp{Link: %+v}", l.Link) + return fmt.Sprintf("LinkAtResp{Link: %s}", l.Link.String()) } // FStatFSReq is used to request StatFS results for the specified FD. @@ -1336,7 +1366,7 @@ type BindAtResp struct { // String implements fmt.Stringer.String. func (b *BindAtResp) String() string { - return fmt.Sprintf("BindAtResp{Child: %+v, BoundSocketFD: %v}", b.Child, b.BoundSocketFD) + return fmt.Sprintf("BindAtResp{Child: %s, BoundSocketFD: %d}", b.Child.String(), b.BoundSocketFD) } // ListenReq is used to make Listen requests. @@ -1571,7 +1601,16 @@ type Getdents64Resp struct { // String implements fmt.Stringer.String. func (g *Getdents64Resp) String() string { - return fmt.Sprintf("Getdents64Resp{Dirents: %+v}", g.Dirents) + var b strings.Builder + b.WriteString("[") + for i, dirent := range g.Dirents { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(dirent.String()) + } + b.WriteString("]") + return fmt.Sprintf("Getdents64Resp{Dirents: %s}", b.String()) } // SizeBytes implements marshal.Marshallable.SizeBytes. From 6669003321e402bcb270300aefa1f916e3c2fbb4 Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Wed, 15 Mar 2023 12:50:56 -0700 Subject: [PATCH 14/49] Make HostBountEndpoint.SetBoundSocketFD take ownership of bound socket FD. Earlier SetBoundSocketFD() was taking ownership of bound socket FD only on success. Having it take ownership unconditionally is cleaner. PiperOrigin-RevId: 516903597 --- pkg/sentry/fsimpl/gofer/directfs_dentry.go | 3 +-- pkg/sentry/fsimpl/gofer/lisafs_dentry.go | 3 +-- pkg/sentry/socket/unix/transport/connectioned.go | 3 ++- pkg/sentry/socket/unix/transport/unix.go | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pkg/sentry/fsimpl/gofer/directfs_dentry.go b/pkg/sentry/fsimpl/gofer/directfs_dentry.go index 74e910a27..934a183db 100644 --- a/pkg/sentry/fsimpl/gofer/directfs_dentry.go +++ b/pkg/sentry/fsimpl/gofer/directfs_dentry.go @@ -476,8 +476,7 @@ func (d *directfsDentry) bindAt(ctx context.Context, name string, creds *auth.Cr } bsFD := &boundSocketFD{sockFD} hbep := opts.Endpoint.(transport.HostBoundEndpoint) - if err := hbep.SetBoundSocketFD(bsFD); err != nil { - bsFD.Close(ctx) + if err := hbep.SetBoundSocketFD(ctx, bsFD); err != nil { return nil, err } diff --git a/pkg/sentry/fsimpl/gofer/lisafs_dentry.go b/pkg/sentry/fsimpl/gofer/lisafs_dentry.go index e2a11f938..045d7b630 100644 --- a/pkg/sentry/fsimpl/gofer/lisafs_dentry.go +++ b/pkg/sentry/fsimpl/gofer/lisafs_dentry.go @@ -406,8 +406,7 @@ func (d *lisafsDentry) mknod(ctx context.Context, name string, creds *auth.Crede return nil, err } hbep := opts.Endpoint.(transport.HostBoundEndpoint) - if err := hbep.SetBoundSocketFD(boundSocketFD); err != nil { - boundSocketFD.Close(ctx) + if err := hbep.SetBoundSocketFD(ctx, boundSocketFD); err != nil { if err := d.controlFD.UnlinkAt(ctx, name, 0 /* flags */); err != nil { log.Warningf("failed to clean up socket which was created by BindAt RPC: %v", err) } diff --git a/pkg/sentry/socket/unix/transport/connectioned.go b/pkg/sentry/socket/unix/transport/connectioned.go index e2698de2d..87f9bb460 100644 --- a/pkg/sentry/socket/unix/transport/connectioned.go +++ b/pkg/sentry/socket/unix/transport/connectioned.go @@ -595,10 +595,11 @@ func (e *connectionedEndpoint) OnSetSendBufferSize(v int64) (newSz int64) { func (e *connectionedEndpoint) WakeupWriters() {} // SetBoundSocketFD implement HostBountEndpoint.SetBoundSocketFD. -func (e *connectionedEndpoint) SetBoundSocketFD(bsFD BoundSocketFD) error { +func (e *connectionedEndpoint) SetBoundSocketFD(ctx context.Context, bsFD BoundSocketFD) error { e.Lock() defer e.Unlock() if e.path != "" || e.boundSocketFD != nil { + bsFD.Close(ctx) return syserr.ErrAlreadyBound.ToError() } e.boundSocketFD = bsFD diff --git a/pkg/sentry/socket/unix/transport/unix.go b/pkg/sentry/socket/unix/transport/unix.go index 2e3135069..ce1e96616 100644 --- a/pkg/sentry/socket/unix/transport/unix.go +++ b/pkg/sentry/socket/unix/transport/unix.go @@ -269,9 +269,9 @@ type BoundEndpoint interface { type HostBoundEndpoint interface { // SetBoundSocketFD will be called on supporting endpoints after // binding a socket on the host filesystem. Implementations should - // delegate Listen and Accept calls to the BoundSocketFD. On success, - // the ownership of bsFD is transferred to the endpoint. - SetBoundSocketFD(bsFD BoundSocketFD) error + // delegate Listen and Accept calls to the BoundSocketFD. The ownership + // of bsFD is transferred to the endpoint. + SetBoundSocketFD(ctx context.Context, bsFD BoundSocketFD) error // ResetBoundSocketFD cleans up the BoundSocketFD set by the last successful // SetBoundSocketFD call. From 8fb66eb28944668c56d9aa8df961c91ec344f876 Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Wed, 15 Mar 2023 13:23:20 -0700 Subject: [PATCH 15/49] Get HardLink test in //runsc/fsgofer:lisafs_test to pass. This test was attempting to create a link with the same name as the target. Changed that to use a different name for link. This test also exposed a ref count bug in LISAFS RPC handlers. Fixed that. Updates #8688 PiperOrigin-RevId: 516912550 --- pkg/lisafs/handlers.go | 1 + pkg/lisafs/testsuite/testsuite.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/lisafs/handlers.go b/pkg/lisafs/handlers.go index cb7167bb7..9f3f9c1c6 100644 --- a/pkg/lisafs/handlers.go +++ b/pkg/lisafs/handlers.go @@ -892,6 +892,7 @@ func LinkAtHandler(c *Connection, comm Communicator, payloadLen uint32) (uint32, if err != nil { return 0, err } + defer targetFD.DecRef(nil) if targetFD.IsDir() { // Can not create hard link to directory. return 0, unix.EPERM diff --git a/pkg/lisafs/testsuite/testsuite.go b/pkg/lisafs/testsuite/testsuite.go index e9815baa2..0ae4db28d 100644 --- a/pkg/lisafs/testsuite/testsuite.go +++ b/pkg/lisafs/testsuite/testsuite.go @@ -490,7 +490,8 @@ func testHardLink(ctx context.Context, t *testing.T, tester Tester, root lisafs. defer closeFD(ctx, t, fd) defer unix.Close(hostFD) - link, linkStat := link(ctx, t, root, name, controlFile) + linkName := "linkFile" + link, linkStat := link(ctx, t, root, linkName, controlFile) defer closeFD(ctx, t, link) if linkStat.Ino != fileIno.Ino { From d2da7d77d27d4d80586b64f74cd837fa7454e6ce Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Wed, 15 Mar 2023 14:31:15 -0700 Subject: [PATCH 16/49] Get rid of fchdir(2) usage in directfs. fchdir(2) was only needed to support connect(2) and bind(2), which are the only filesystem operations that force host path traversal. The sandbox process does not have the container filesystem in its mount namespace. It does not even have procfs, so we can't use a path like /proc/self/fd/{socket-fd}. So earlier we were using fchdir(2) to go into the socket's parent directory and use a relative path. However, allowing fchdir(2) makes it harder to reason about the sandbox process state because it modifies the process's CWD. Directfs seccomp filters today do not allow the usage of AT_FDCWD, but that could change in the future. Operations that rely on the sandbox CWD need to synchronize using pkg/sentry/fsutil/chdir package, like directfs does today. If they don't then we could have nasty bugs. Now we fallback to using LISAFS in such scenarios. This makes bind(2) and connect(2) a little slower because now directfs has to perform a LISAFS walk to get a LISAFS FD to the socket and then make the Connect/Bind RPC. But this allows us to remove fchdir, socket, connect, bind, listen and accept from the directfs seccomp filters. The rationale is that if making 2 relatively-rare operations slightly slower helps us avoid chdir(2) and these other socket-based syscalls, then it is overall a win. Reported-by: Etienne Perot PiperOrigin-RevId: 516931050 --- pkg/sentry/fsimpl/gofer/BUILD | 1 - pkg/sentry/fsimpl/gofer/dentry_impl.go | 2 +- pkg/sentry/fsimpl/gofer/directfs_dentry.go | 197 +++++++++------------ pkg/sentry/fsimpl/gofer/filesystem.go | 6 - pkg/sentry/fsimpl/gofer/gofer.go | 20 +-- pkg/sentry/fsutil/chdir/BUILD | 13 -- pkg/sentry/fsutil/chdir/chdir.go | 69 -------- runsc/boot/filter/config.go | 72 -------- runsc/boot/filter/filter.go | 12 -- runsc/boot/loader.go | 3 - runsc/boot/vfs.go | 7 - runsc/cmd/BUILD | 1 - runsc/cmd/boot.go | 7 - 13 files changed, 89 insertions(+), 321 deletions(-) delete mode 100644 pkg/sentry/fsutil/chdir/BUILD delete mode 100644 pkg/sentry/fsutil/chdir/chdir.go diff --git a/pkg/sentry/fsimpl/gofer/BUILD b/pkg/sentry/fsimpl/gofer/BUILD index 8b48bdb13..01c5b41c0 100644 --- a/pkg/sentry/fsimpl/gofer/BUILD +++ b/pkg/sentry/fsimpl/gofer/BUILD @@ -98,7 +98,6 @@ go_library( "//pkg/sentry/fsimpl/lock", "//pkg/sentry/fsmetric", "//pkg/sentry/fsutil", - "//pkg/sentry/fsutil/chdir", "//pkg/sentry/hostfd", "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", diff --git a/pkg/sentry/fsimpl/gofer/dentry_impl.go b/pkg/sentry/fsimpl/gofer/dentry_impl.go index b5728f359..c6bceab20 100644 --- a/pkg/sentry/fsimpl/gofer/dentry_impl.go +++ b/pkg/sentry/fsimpl/gofer/dentry_impl.go @@ -490,7 +490,7 @@ func (fs *filesystem) restoreRoot(ctx context.Context, opts *vfs.CompleteRestore case *lisafsDentry: return dt.restoreFile(ctx, &rootInode, opts) case *directfsDentry: - dt.rootControlFDLisa = fs.client.NewFD(rootInode.ControlFD) + dt.controlFDLisa = fs.client.NewFD(rootInode.ControlFD) return dt.restoreFile(ctx, rootHostFD, opts) default: panic("unknown dentry implementation") diff --git a/pkg/sentry/fsimpl/gofer/directfs_dentry.go b/pkg/sentry/fsimpl/gofer/directfs_dentry.go index 934a183db..232c872fd 100644 --- a/pkg/sentry/fsimpl/gofer/directfs_dentry.go +++ b/pkg/sentry/fsimpl/gofer/directfs_dentry.go @@ -27,7 +27,6 @@ import ( "gvisor.dev/gvisor/pkg/fsutil" "gvisor.dev/gvisor/pkg/lisafs" "gvisor.dev/gvisor/pkg/log" - "gvisor.dev/gvisor/pkg/sentry/fsutil/chdir" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/socket/unix/transport" "gvisor.dev/gvisor/pkg/sentry/vfs" @@ -78,7 +77,7 @@ func (fs *filesystem) getDirectfsRootDentry(ctx context.Context, rootHostFD int, rootControlFD.Close(ctx, false /* flush */) return nil, err } - d.impl.(*directfsDentry).rootControlFDLisa = rootControlFD + d.impl.(*directfsDentry).controlFDLisa = rootControlFD return d, nil } @@ -95,12 +94,16 @@ type directfsDentry struct { // controlFD is the host FD to this file. controlFD is immutable. controlFD int - // rootControlFDLisa is a lisafs control FD on this dentry. This is only set - // when this dentry represents the root of the current mount. This is - // required in cases where we require dentry.parent to perform operations. - // But for the root dentry, the parent is not available. So we fallback to - // using lisafs RPCs. rootControlFDLisa is immutable. - rootControlFDLisa lisafs.ClientFD `state:"nosave"` + // controlFDLisa is a lisafs control FD on this dentry. + // This is used to fallback to using lisafs RPCs in the following cases: + // * When parent dentry is required to perform operations but + // dentry.parent = nil (root dentry). + // * For path-based syscalls (like connect(2) and bind(2)) on sockets. + // + // For the root dentry, controlFDLisa is always set and is immutable. + // For sockets, controlFDLisa is protected by dentry.handleMu and is + // immutable after initialization. + controlFDLisa lisafs.ClientFD `state:"nosave"` } // newDirectfsDentry creates a new dentry representing the given file. The dentry @@ -147,10 +150,10 @@ func (fs *filesystem) newDirectfsDentry(controlFD int) (*dentry, error) { func (d *directfsDentry) openHandle(ctx context.Context, flags uint32) (handle, error) { if d.parent == nil { // This is a mount point. We don't have parent. Fallback to using lisafs. - if !d.rootControlFDLisa.Ok() { - panic("directfsDentry.rootControlFDLisa is not set for mount point file") + if !d.controlFDLisa.Ok() { + panic("directfsDentry.controlFDLisa is not set for mount point dentry") } - openFD, hostFD, err := d.rootControlFDLisa.OpenAt(ctx, flags) + openFD, hostFD, err := d.controlFDLisa.OpenAt(ctx, flags) if err != nil { return noHandle, err } @@ -172,6 +175,55 @@ func (d *directfsDentry) openHandle(ctx context.Context, flags uint32) (handle, return handle{fd: int32(openFD)}, nil } +// Precondition: fs.renameMu is locked. +func (d *directfsDentry) ensureLisafsControlFD(ctx context.Context) error { + d.handleMu.Lock() + defer d.handleMu.Unlock() + if d.controlFDLisa.Ok() { + return nil + } + + var names []string + root := d + for root.parent != nil { + names = append(names, root.name) + root = root.parent.impl.(*directfsDentry) + } + if !root.controlFDLisa.Ok() { + panic("controlFDLisa is not set for mount point dentry") + } + if len(names) == 0 { + return nil // d == root + } + // Reverse names. + last := len(names) - 1 + for i := 0; i < len(names)/2; i++ { + names[i], names[last-i] = names[last-i], names[i] + } + status, inodes, err := root.controlFDLisa.WalkMultiple(ctx, names) + if err != nil { + return err + } + defer func() { + // Close everything except for inodes[last] if it exists. + for i := 0; i < len(inodes) && i < last; i++ { + flush := i == last-1 || i == len(inodes)-1 + d.fs.client.CloseFD(ctx, inodes[i].ControlFD, flush) + } + }() + switch status { + case lisafs.WalkComponentDoesNotExist: + return unix.ENOENT + case lisafs.WalkComponentSymlink: + log.Warningf("intermediate path component was a symlink? names = %v, inodes = %+v", names, inodes) + return unix.ELOOP + case lisafs.WalkSuccess: + d.controlFDLisa = d.fs.client.NewFD(inodes[last].ControlFD) + return nil + } + panic("unreachable") +} + // Precondition: d.metadataMu must be locked. // // +checklocks:d.metadataMu @@ -226,11 +278,11 @@ func (d *directfsDentry) chmod(ctx context.Context, mode uint16) error { // This is a mount point socket. We don't have a parent FD. Fallback to using // lisafs. - if !d.rootControlFDLisa.Ok() { - panic("directfsDentry.rootControlFDLisa is not set for mount point socket") + if !d.controlFDLisa.Ok() { + panic("directfsDentry.controlFDLisa is not set for mount point socket") } - return chmod(ctx, d.rootControlFDLisa, mode) + return chmod(ctx, d.controlFDLisa, mode) } // Preconditions: @@ -274,8 +326,8 @@ func (d *directfsDentry) utimensat(ctx context.Context, stat *linux.Statx) error // This is a mount point symlink. We don't have a parent FD. Fallback to // using lisafs. - if !d.rootControlFDLisa.Ok() { - panic("directfsDentry.rootControlFDLisa is not set for mount point symlink") + if !d.controlFDLisa.Ok() { + panic("directfsDentry.controlFDLisa is not set for mount point symlink") } setStat := linux.Statx{ @@ -283,7 +335,7 @@ func (d *directfsDentry) utimensat(ctx context.Context, stat *linux.Statx) error Atime: stat.Atime, Mtime: stat.Mtime, } - _, failureErr, err := d.rootControlFDLisa.SetStat(ctx, &setStat) + _, failureErr, err := d.controlFDLisa.SetStat(ctx, &setStat) if err != nil { return err } @@ -352,8 +404,8 @@ func (d *directfsDentry) destroy(ctx context.Context) { if d.controlFD >= 0 { _ = unix.Close(d.controlFD) } - if d.rootControlFDLisa.Ok() { - d.rootControlFDLisa.Close(ctx, true /* flush */) + if d.controlFDLisa.Ok() { + d.controlFDLisa.Close(ctx, true /* flush */) } } @@ -428,75 +480,29 @@ func (d *directfsDentry) mknod(ctx context.Context, name string, creds *auth.Cre return d.getCreatedChild(name, int(creds.EffectiveKUID), int(creds.EffectiveKGID), false /* isDir */) } -type boundSocketFD struct { - sock int -} - -// Close closes the host and gofer-backed FDs associated to this bound socket. -func (fd *boundSocketFD) Close(ctx context.Context) { - _ = unix.Close(fd.sock) -} - -// NotificationFD is a host FD that can be used to notify when new clients -// connect to the socket. -func (fd *boundSocketFD) NotificationFD() int32 { - return int32(fd.sock) -} - -// Listen makes a Listen RPC. -func (fd *boundSocketFD) Listen(ctx context.Context, backlog int32) error { - return unix.Listen(int(fd.sock), int(backlog)) -} - -// Accept makes an Accept RPC. -func (fd *boundSocketFD) Accept(ctx context.Context) (int, error) { - flags := unix.O_NONBLOCK | unix.O_CLOEXEC - nfd, _, err := unix.Accept4(int(fd.sock), flags) - if err != nil { - return -1, err - } - return nfd, nil -} - // Precondition: opts.Endpoint != nil and is transport.HostBoundEndpoint type. func (d *directfsDentry) bindAt(ctx context.Context, name string, creds *auth.Credentials, opts *vfs.MknodOptions) (*dentry, error) { - if !d.fs.opts.directfs.hostUDSBind { - return nil, unix.EPERM + // There are no filesystems mounted in the sandbox process's mount namespace. + // So we can't perform absolute path traversals. So fallback to using lisafs. + if err := d.ensureLisafsControlFD(ctx); err != nil { + return nil, err } - - // This mknod(2) is coming from unix bind(2), as opts.Endpoint is set. sockType := opts.Endpoint.(transport.Endpoint).Type() - if !isSocketTypeSupported(sockType) { - return nil, unix.ENXIO - } - // Create the socket. - sockFD, err := unix.Socket(unix.AF_UNIX, int(sockType), 0) + childInode, boundSocketFD, err := d.controlFDLisa.BindAt(ctx, sockType, name, opts.Mode, lisafs.UID(creds.EffectiveKUID), lisafs.GID(creds.EffectiveKGID)) if err != nil { return nil, err } - bsFD := &boundSocketFD{sockFD} + d.fs.client.CloseFD(ctx, childInode.ControlFD, true /* flush */) + // Update opts.Endpoint that it is bound. hbep := opts.Endpoint.(transport.HostBoundEndpoint) - if err := hbep.SetBoundSocketFD(ctx, bsFD); err != nil { + if err := hbep.SetBoundSocketFD(ctx, boundSocketFD); err != nil { + if err := unix.Unlinkat(d.controlFD, name, 0); err != nil { + log.Warningf("error unlinking newly created socket %q after failure: %v", filepath.Join(genericDebugPathname(&d.dentry), name), err) + } return nil, err } - - // fchmod(2) has to happen *before* the bind(2). sockFD's file mode will - // be used in creating the filesystem-object in bind(2). - if err := unix.Fchmod(sockFD, uint32(opts.Mode&^unix.S_IFMT)); err != nil { - hbep.ResetBoundSocketFD(ctx) - return nil, err - } - - // There are no filesystems mounted in the sandbox process's mount namespace. - // So we can't perform absolute path traversals. So fchdir(2) to this - // directory and bind at name (relative path traversal). - if err := chdir.DoInDir(d.controlFD, func() error { - return unix.Bind(sockFD, &unix.SockaddrUnix{Name: name}) - }); err != nil { - hbep.ResetBoundSocketFD(ctx) - return nil, err - } - child, err := d.getCreatedChild(name, int(creds.EffectiveKUID), int(creds.EffectiveKGID), false /* isDir */) + // Socket already has the right UID/GID set, so use uid = gid = -1. + child, err := d.getCreatedChild(name, -1 /* uid */, -1 /* gid */, false /* isDir */) if err != nil { hbep.ResetBoundSocketFD(ctx) return nil, err @@ -590,41 +596,12 @@ func (d *directfsDentry) getDirentsLocked(count int, recordDirent func(name stri // Precondition: fs.renameMu is locked. func (d *directfsDentry) connect(ctx context.Context, sockType linux.SockType) (int, error) { - if !d.fs.opts.directfs.hostUDSConnect { - return -1, unix.EPERM - } - - if d.parent == nil { - // This is a mount point socket. Fall back to lisafs for connect since we - // don't have parent. - if !d.rootControlFDLisa.Ok() { - panic("directfsDentry.rootControlFDLisa is not set for mount point socket") - } - return d.rootControlFDLisa.Connect(ctx, sockType) - } - - if !isSocketTypeSupported(sockType) { - log.Warningf("unsupported socket type %d", sockType) - return -1, unix.ENXIO - } - - sock, err := unix.Socket(unix.AF_UNIX, int(sockType), 0) - if err != nil { - log.Warningf("socket(2) failed: %v", err) - return -1, err - } - // There are no filesystems mounted in the sandbox process's mount namespace. - // So we can't perform absolute path traversals. So fchdir(2) to parent - // and connect to this socket at name (relative path traversal). - if err := chdir.DoInDir(d.parent.impl.(*directfsDentry).controlFD, func() error { - return unix.Connect(sock, &unix.SockaddrUnix{Name: d.name}) - }); err != nil { - unix.Close(sock) - log.Warningf("connect(2) failed: %v", err) + // So we can't perform absolute path traversals. So fallback to using lisafs. + if err := d.ensureLisafsControlFD(ctx); err != nil { return -1, err } - return sock, nil + return d.controlFDLisa.Connect(ctx, sockType) } func (d *directfsDentry) readlink() (string, error) { diff --git a/pkg/sentry/fsimpl/gofer/filesystem.go b/pkg/sentry/fsimpl/gofer/filesystem.go index d32cdb37a..97cbdde45 100644 --- a/pkg/sentry/fsimpl/gofer/filesystem.go +++ b/pkg/sentry/fsimpl/gofer/filesystem.go @@ -1753,12 +1753,6 @@ func (fs *filesystem) MountOptions() string { } if fs.opts.directfs.enabled { optsKV = append(optsKV, mopt{moptDirectfs, nil}) - if fs.opts.directfs.hostUDSBind { - optsKV = append(optsKV, mopt{moptHostUDSBind, nil}) - } - if fs.opts.directfs.hostUDSConnect { - optsKV = append(optsKV, mopt{moptHostUDSConnect, nil}) - } } opts := make([]string, 0, len(optsKV)) diff --git a/pkg/sentry/fsimpl/gofer/gofer.go b/pkg/sentry/fsimpl/gofer/gofer.go index 2aa95b0a3..73825ddbb 100644 --- a/pkg/sentry/fsimpl/gofer/gofer.go +++ b/pkg/sentry/fsimpl/gofer/gofer.go @@ -85,9 +85,7 @@ const ( moptOverlayfsStaleRead = "overlayfs_stale_read" // Directfs options. - moptDirectfs = "directfs" - moptHostUDSConnect = "host_uds_connect" - moptHostUDSBind = "host_uds_bind" + moptDirectfs = "directfs" ) // Valid values for the "cache" mount option. @@ -287,14 +285,6 @@ type directfsOpts struct { // If directfs is enabled, the gofer client does not make RPCs to the gofer // process. Instead, it makes host syscalls to perform file operations. enabled bool - - // hostUDSBind dictates whether this mount can create host unix domain - // sockets. - hostUDSBind bool - - // hostUDSConnect dictates whether this mount can connect to host unix domain - // sockets. - hostUDSConnect bool } // InteropMode controls the client's interaction with other remote filesystem @@ -485,14 +475,6 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt delete(mopts, moptDirectfs) fsopts.directfs.enabled = true } - if _, ok := mopts[moptHostUDSBind]; ok { - delete(mopts, moptHostUDSBind) - fsopts.directfs.hostUDSBind = true - } - if _, ok := mopts[moptHostUDSConnect]; ok { - delete(mopts, moptHostUDSConnect) - fsopts.directfs.hostUDSConnect = true - } // fsopts.regularFilesUseSpecialFileFD can only be enabled by specifying // "cache=none". diff --git a/pkg/sentry/fsutil/chdir/BUILD b/pkg/sentry/fsutil/chdir/BUILD deleted file mode 100644 index 98db91e10..000000000 --- a/pkg/sentry/fsutil/chdir/BUILD +++ /dev/null @@ -1,13 +0,0 @@ -load("//tools:defs.bzl", "go_library") - -package(licenses = ["notice"]) - -go_library( - name = "chdir", - srcs = ["chdir.go"], - visibility = ["//pkg/sentry:internal"], - deps = [ - "//pkg/sync", - "@org_golang_x_sys//unix:go_default_library", - ], -) diff --git a/pkg/sentry/fsutil/chdir/chdir.go b/pkg/sentry/fsutil/chdir/chdir.go deleted file mode 100644 index ac1fca9f9..000000000 --- a/pkg/sentry/fsutil/chdir/chdir.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2023 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package chdir provides utilities to control the sandbox process's current -// working directory. -package chdir - -import ( - "fmt" - "os" - - "golang.org/x/sys/unix" - "gvisor.dev/gvisor/pkg/sync" -) - -// chdirMu is the global mutex that synchronizes host chdir operations for the -// sandbox process. -var chdirMu sync.Mutex - -// cwd is the current working directory for the sandbox process. The sandbox -// process usually runs in an empty chroot so cwd should be pointing to '/'. -// cwd is protected by chdirMu. -var cwd *os.File - -// InitCWD initializes the global cwd FD. InitCWD must be called after the -// sandbox process has been configured with pivot_root(2)/chroot(2). -func InitCWD() (err error) { - chdirMu.Lock() - defer chdirMu.Unlock() - if cwd != nil { - panic("InitCWD() called twice") - } - cwd, err = os.Open(".") - return -} - -// DoInDir performs fn after chdir-ing to dirFD and then reverts back to the -// original CWD. -// -// Precondition: InitCWD() must have been called. -func DoInDir(dirFD int, fn func() error) error { - chdirMu.Lock() - defer chdirMu.Unlock() - if cwd == nil { - panic("DoInDir() called without calling InitCWD()") - } - - defer func() { - if err := unix.Fchdir(int(cwd.Fd())); err != nil { - panic(fmt.Errorf("restoring orginial CWD failed: %v", err)) - } - }() - - if err := unix.Fchdir(dirFD); err != nil { - return err - } - return fn() -} diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go index 85fde7ddf..30a038a63 100644 --- a/runsc/boot/filter/config.go +++ b/runsc/boot/filter/config.go @@ -457,11 +457,6 @@ func hostFilesystemFilters() seccomp.SyscallRules { seccomp.MatchAny{}, }, }, - unix.SYS_FCHDIR: []seccomp.Rule{ - { - validFDCheck, - }, - }, unix.SYS_READLINKAT: []seccomp.Rule{ { validFDCheck, @@ -496,70 +491,3 @@ func hostFilesystemFilters() seccomp.SyscallRules { }, } } - -// hostSocketCommonFilters contains syscalls that are needed to create socket FDs. -func hostSocketCommonFilters() seccomp.SyscallRules { - return seccomp.SyscallRules{ - unix.SYS_SOCKET: []seccomp.Rule{ - { - seccomp.EqualTo(unix.AF_UNIX), - seccomp.EqualTo(unix.SOCK_STREAM), - seccomp.EqualTo(0), - }, - { - seccomp.EqualTo(unix.AF_UNIX), - seccomp.EqualTo(unix.SOCK_DGRAM), - seccomp.EqualTo(0), - }, - { - seccomp.EqualTo(unix.AF_UNIX), - seccomp.EqualTo(unix.SOCK_SEQPACKET), - seccomp.EqualTo(0), - }, - }, - } -} - -// hostSocketCreateFilters contains syscalls that are needed to create UDS on -// the host filesystem and interact with it. -func hostSocketCreateFilters() seccomp.SyscallRules { - validFDCheck := nonNegativeFDCheck() - return seccomp.SyscallRules{ - unix.SYS_BIND: []seccomp.Rule{ - { - validFDCheck, - seccomp.MatchAny{}, - seccomp.MatchAny{}, - }, - }, - unix.SYS_LISTEN: []seccomp.Rule{ - { - validFDCheck, - seccomp.MatchAny{}, - }, - }, - unix.SYS_ACCEPT4: []seccomp.Rule{ - { - validFDCheck, - seccomp.MatchAny{}, - seccomp.MatchAny{}, - seccomp.EqualTo(unix.SOCK_NONBLOCK | unix.SOCK_CLOEXEC), - }, - }, - } -} - -// hostSocketOpenFilters contains syscalls that are needed to open UDS on the -// host filesystem and interact with it. -func hostSocketOpenFilters() seccomp.SyscallRules { - validFDCheck := nonNegativeFDCheck() - return seccomp.SyscallRules{ - unix.SYS_CONNECT: []seccomp.Rule{ - { - validFDCheck, - seccomp.MatchAny{}, - seccomp.MatchAny{}, - }, - }, - } -} diff --git a/runsc/boot/filter/filter.go b/runsc/boot/filter/filter.go index b7c3bfcbb..b3a93fc38 100644 --- a/runsc/boot/filter/filter.go +++ b/runsc/boot/filter/filter.go @@ -29,8 +29,6 @@ type Options struct { HostNetwork bool HostNetworkRawSockets bool HostFilesystem bool - HostSocketCreate bool - HostSocketOpen bool ProfileEnable bool ControllerFD int } @@ -60,16 +58,6 @@ func Install(opt Options) error { Report("host filesystem enabled: syscall filters less restrictive!") s.Merge(hostFilesystemFilters()) } - if opt.HostSocketCreate || opt.HostSocketOpen { - Report("host socket enabled: syscall filters less restrictive!") - s.Merge(hostSocketCommonFilters()) - if opt.HostSocketCreate { - s.Merge(hostSocketCreateFilters()) - } - if opt.HostSocketOpen { - s.Merge(hostSocketOpenFilters()) - } - } s.Merge(opt.Platform.SyscallFilters()) diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 1e2649957..6afc87404 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -601,15 +601,12 @@ func (l *Loader) installSeccompFilters() error { if l.root.conf.DisableSeccomp { filter.Report("syscall filter is DISABLED. Running in less secure mode.") } else { - hostUDS := l.root.conf.GetHostUDS() hostnet := l.root.conf.Network == config.NetworkHost opts := filter.Options{ Platform: l.k.Platform, HostNetwork: hostnet, HostNetworkRawSockets: hostnet && l.root.conf.EnableRaw, HostFilesystem: l.root.conf.DirectFS, - HostSocketCreate: l.root.conf.DirectFS && hostUDS.AllowCreate(), - HostSocketOpen: l.root.conf.DirectFS && hostUDS.AllowOpen(), ProfileEnable: l.root.conf.ProfileEnable, ControllerFD: l.ctrl.srv.FD(), } diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go index 88e0cd946..4c59859ca 100644 --- a/runsc/boot/vfs.go +++ b/runsc/boot/vfs.go @@ -282,13 +282,6 @@ func goferMountData(fd int, fa config.FileAccessType, conf *config.Config) []str } if conf.DirectFS { opts = append(opts, "directfs") - hostUDS := conf.GetHostUDS() - if hostUDS.AllowOpen() { - opts = append(opts, "host_uds_connect") - } - if hostUDS.AllowCreate() { - opts = append(opts, "host_uds_bind") - } } return opts } diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 96e81a4ae..89f602fdb 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -61,7 +61,6 @@ go_library( "//pkg/prometheus", "//pkg/ring0", "//pkg/sentry/control", - "//pkg/sentry/fsutil/chdir", "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/platform", diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index 496400398..af25bccb1 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -32,7 +32,6 @@ import ( "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/metric" "gvisor.dev/gvisor/pkg/ring0" - "gvisor.dev/gvisor/pkg/sentry/fsutil/chdir" "gvisor.dev/gvisor/pkg/sentry/platform" "gvisor.dev/gvisor/runsc/boot" "gvisor.dev/gvisor/runsc/cmd/util" @@ -362,12 +361,6 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomma // modes exactly as sent by the sentry, which would have already applied // the application umask. unix.Umask(0) - - // Now that the sandbox process is running in an empty pivot_root(2) - // environment, we can initialize the chdir package. - if err := chdir.InitCWD(); err != nil { - util.Fatalf("Failed to initialize CWD for directfs: %v", err) - } } if conf.EnableCoreTags { From 6f90845aec92c2eb85a6119727c32207b0b9f77f Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Wed, 15 Mar 2023 14:54:16 -0700 Subject: [PATCH 17/49] Automated rollback of changelist 516870932 PiperOrigin-RevId: 516937389 --- pkg/tcpip/stack/gro.go | 5 ++--- runsc/boot/network.go | 20 +++++++------------- runsc/sandbox/network.go | 16 ++++++---------- test/runner/main.go | 1 - 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/pkg/tcpip/stack/gro.go b/pkg/tcpip/stack/gro.go index 29a1b7061..a3b708fd5 100644 --- a/pkg/tcpip/stack/gro.go +++ b/pkg/tcpip/stack/gro.go @@ -226,8 +226,6 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo // Flush groPkt or merge the packets. pktSize := pkt.Data().Size() flags := tcpHdr.Flags() - dataOff := tcpHdr.DataOffset() - tcpPayloadSize := pkt.Data().Size() - len(ipHdr) - int(dataOff) if flushGROPkt { // Flush the existing GRO packet. Don't hold bucket.mu while // processing the packet. @@ -241,10 +239,12 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo } else if groPkt != nil { // Merge pkt in to GRO packet. buf := pkt.Data().ToBuffer() + dataOff := tcpHdr.DataOffset() buf.TrimFront(int64(len(ipHdr)) + int64(dataOff)) groPkt.pkt.Data().MergeBuffer(&buf) buf.Release() // Update the IP total length. + tcpPayloadSize := pkt.Data().Size() - len(ipHdr) - int(dataOff) updateIPHdr(groPkt.ipHdr, tcpPayloadSize) // Add flags from the packet to the GRO packet. groPkt.tcpHdr.SetFlags(uint8(groPkt.tcpHdr.Flags() | (flags & (header.TCPFlagFin | header.TCPFlagPsh)))) @@ -261,7 +261,6 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo // malformed, a local GSO packet, or has already been handled by host // GRO. flush := header.TCPFlags(flags)&(header.TCPFlagUrg|header.TCPFlagPsh|header.TCPFlagRst|header.TCPFlagSyn|header.TCPFlagFin) != 0 - flush = flush || tcpPayloadSize == 0 if groPkt != nil { flush = flush || pktSize != groPkt.initialLength } diff --git a/runsc/boot/network.go b/runsc/boot/network.go index 4f9c2d112..b7b34c630 100644 --- a/runsc/boot/network.go +++ b/runsc/boot/network.go @@ -121,19 +121,17 @@ type XDPLink struct { LinkAddress net.HardwareAddr QDisc config.QueueingDiscipline Neighbors []Neighbor - GvisorGROTimeout time.Duration // NumChannels controls how many underlying FDs are to be used to // create this endpoint. NumChannels int } -// LoopbackLink configures a loopback link. +// LoopbackLink configures a loopback li nk. type LoopbackLink struct { - Name string - Addresses []IPWithPrefix - Routes []Route - GvisorGROTimeout time.Duration + Name string + Addresses []IPWithPrefix + Routes []Route } // CreateLinksAndRoutesArgs are arguments to CreateLinkAndRoutes. @@ -218,10 +216,7 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct linkEP := packetsocket.New(ethernet.New(loopback.New())) log.Infof("Enabling loopback interface %q with id %d on addresses %+v", link.Name, nicID, link.Addresses) - opts := stack.NICOptions{ - Name: link.Name, - GROTimeout: link.GvisorGROTimeout, - } + opts := stack.NICOptions{Name: link.Name} if err := n.createNICWithAddrs(nicID, linkEP, opts, link.Addresses); err != nil { return err } @@ -388,9 +383,8 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct log.Infof("Enabling interface %q with id %d on addresses %+v (%v) w/ %d channels", link.Name, nicID, link.Addresses, mac, link.NumChannels) opts := stack.NICOptions{ - Name: link.Name, - QDisc: qDisc, - GROTimeout: link.GvisorGROTimeout, + Name: link.Name, + QDisc: qDisc, } if err := n.createNICWithAddrs(nicID, sniffEP, opts, link.Addresses); err != nil { return err diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index 5a9773f19..f5b1a00b3 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -60,7 +60,7 @@ func setupNetwork(conn *urpc.Client, pid int, conf *config.Config) error { switch conf.Network { case config.NetworkNone: log.Infof("Network is disabled, create loopback interface only") - if err := createDefaultLoopbackInterface(conf, conn); err != nil { + if err := createDefaultLoopbackInterface(conn); err != nil { return fmt.Errorf("creating default loopback interface: %v", err) } case config.NetworkSandbox: @@ -78,11 +78,9 @@ func setupNetwork(conn *urpc.Client, pid int, conf *config.Config) error { return nil } -func createDefaultLoopbackInterface(conf *config.Config, conn *urpc.Client) error { - link := boot.DefaultLoopbackLink - link.GvisorGROTimeout = conf.GvisorGROTimeout +func createDefaultLoopbackInterface(conn *urpc.Client) error { if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &boot.CreateLinksAndRoutesArgs{ - LoopbackLinks: []boot.LoopbackLink{link}, + LoopbackLinks: []boot.LoopbackLink{boot.DefaultLoopbackLink}, }, nil); err != nil { return fmt.Errorf("creating loopback link and routes: %v", err) } @@ -159,7 +157,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con // We build our own loopback device. if iface.Flags&net.FlagLoopback != 0 { - link, err := loopbackLink(conf, iface, allAddrs) + link, err := loopbackLink(iface, allAddrs) if err != nil { return fmt.Errorf("getting loopback link for iface %q: %w", iface.Name, err) } @@ -263,7 +261,6 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con Neighbors: neighbors, LinkAddress: linkAddress, Addresses: addresses, - GvisorGROTimeout: conf.GvisorGROTimeout, }) } else { link := boot.FDBasedLink{ @@ -495,10 +492,9 @@ func createSocketXDP(iface net.Interface) ([]*os.File, error) { // loopbackLink returns the link with addresses and routes for a loopback // interface. -func loopbackLink(conf *config.Config, iface net.Interface, addrs []net.Addr) (boot.LoopbackLink, error) { +func loopbackLink(iface net.Interface, addrs []net.Addr) (boot.LoopbackLink, error) { link := boot.LoopbackLink{ - Name: iface.Name, - GvisorGROTimeout: conf.GvisorGROTimeout, + Name: iface.Name, } for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) diff --git a/test/runner/main.go b/test/runner/main.go index 5ad4ceaf7..c017499dd 100644 --- a/test/runner/main.go +++ b/test/runner/main.go @@ -229,7 +229,6 @@ func runRunsc(tc *gtest.TestCase, spec *specs.Spec) error { "-watchdog-action=panic", "-platform", *platform, "-file-access", *fileAccess, - "-gvisor-gro=200000ns", } if *network == "host" && !testutil.TestEnvSupportsRawSockets { From fedadb0932055d0806404be33e3a48852aa989ac Mon Sep 17 00:00:00 2001 From: Konstantin Bogomolov Date: Wed, 15 Mar 2023 19:30:45 -0700 Subject: [PATCH 18/49] Fix syzkaller systrap builds. Reported-by: syzbot+9810b08ace5a190d04ca@syzkaller.appspotmail.com PiperOrigin-RevId: 516993001 --- pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c b/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c index aa4aca9dd..1161f89ad 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c +++ b/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c @@ -385,7 +385,8 @@ void __syshandler() { void asm_restore_state(); // On x86 restore_state jumps straight to user code and does not return. -void restore_state(struct sysmsg *sysmsg, struct thread_context *ctx, void *) { +void restore_state(struct sysmsg *sysmsg, struct thread_context *ctx, + void *unused) { set_fsbase(&ctx->ptregs); asm_restore_state(); } From 758da469f7ed1e70fda62258d77981102f414883 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Thu, 16 Mar 2023 10:25:46 -0700 Subject: [PATCH 19/49] kernel: release kernel.taskSetRWMutex before calling TaskImage.Release Reported-by: syzbot+c58795dc8124b39436a8@syzkaller.appspotmail.com --- pkg/sentry/kernel/task_exec.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/sentry/kernel/task_exec.go b/pkg/sentry/kernel/task_exec.go index 6dda5a2fd..6960c6e6d 100644 --- a/pkg/sentry/kernel/task_exec.go +++ b/pkg/sentry/kernel/task_exec.go @@ -66,6 +66,7 @@ package kernel import ( "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/cleanup" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/sentry/mm" "gvisor.dev/gvisor/pkg/sentry/seccheck" @@ -92,13 +93,16 @@ func (*execStop) Killable() bool { return true } // Preconditions: The caller must be running Task.doSyscallInvoke on the task // goroutine. func (t *Task) Execve(newImage *TaskImage, argv, env []string, executable *vfs.FileDescription, pathname string) (*SyscallControl, error) { + cu := cleanup.Make(func() { + newImage.release() + }) + defer cu.Clean() // We can't clearly hold kernel package locks while stat'ing executable. if seccheck.Global.Enabled(seccheck.PointExecve) { mask, info := getExecveSeccheckInfo(t, argv, env, executable, pathname) if err := seccheck.Global.SentToSinks(func(c seccheck.Sink) error { return c.Execve(t, mask, info) }); err != nil { - newImage.release() return nil, err } } @@ -111,7 +115,6 @@ func (t *Task) Execve(newImage *TaskImage, argv, env []string, executable *vfs.F if t.tg.exiting || t.tg.execing != nil { // We lost to a racing group-exit, kill, or exec from another thread // and should just exit. - newImage.release() return nil, linuxerr.EINTR } @@ -133,6 +136,7 @@ func (t *Task) Execve(newImage *TaskImage, argv, env []string, executable *vfs.F t.beginInternalStopLocked((*execStop)(nil)) } + cu.Release() return &SyscallControl{next: &runSyscallAfterExecStop{newImage}, ignoreReturn: true}, nil } From adde0cc8145fea4be73c73ec704b3578e1c3a35f Mon Sep 17 00:00:00 2001 From: Konstantin Bogomolov Date: Thu, 16 Mar 2023 10:47:23 -0700 Subject: [PATCH 20/49] Refactor context-related shared memory usage. This change introduces an abstraction for most accesses to shared thread-context memory. In general, there are very few cases where accessing this memory is not supposed to be atomic, so it makes sense to abstract these accesses into getters/setters that perform the actions atomically. After this change, we should treat most direct accesses through sharedContext.shared as suspect. Additionally this cleanup allows the new sharedContext instance to become the context interruptor. When doing this it is no longer required to use locks, as was done in context.NotifyInterrupt. PiperOrigin-RevId: 517166307 --- pkg/sentry/platform/systrap/BUILD | 3 +- pkg/sentry/platform/systrap/shared_context.go | 168 ++++++++++++++++++ pkg/sentry/platform/systrap/subprocess.go | 137 +++++--------- .../systrap/subprocess_amd64_unsafe.go | 65 ------- .../systrap/subprocess_arm64_unsafe.go | 65 ------- .../platform/systrap/subprocess_unsafe.go | 41 +++++ pkg/sentry/platform/systrap/sysmsg/sysmsg.go | 16 -- .../platform/systrap/sysmsg/sysmsg_amd64.go | 5 + .../platform/systrap/sysmsg/sysmsg_arm64.go | 5 + pkg/sentry/platform/systrap/systrap.go | 84 +++------ 10 files changed, 287 insertions(+), 302 deletions(-) create mode 100644 pkg/sentry/platform/systrap/shared_context.go delete mode 100644 pkg/sentry/platform/systrap/subprocess_amd64_unsafe.go delete mode 100644 pkg/sentry/platform/systrap/subprocess_arm64_unsafe.go diff --git a/pkg/sentry/platform/systrap/BUILD b/pkg/sentry/platform/systrap/BUILD index bc41e5bf9..b48dc9e42 100644 --- a/pkg/sentry/platform/systrap/BUILD +++ b/pkg/sentry/platform/systrap/BUILD @@ -28,15 +28,14 @@ go_library( "filters_arm64.go", "lib_amd64.s", "lib_arm64.s", + "shared_context.go", "stub_amd64.s", "stub_arm64.s", "stub_defs.go", "stub_unsafe.go", "subprocess.go", "subprocess_amd64.go", - "subprocess_amd64_unsafe.go", "subprocess_arm64.go", - "subprocess_arm64_unsafe.go", "subprocess_linux.go", "subprocess_linux_unsafe.go", "subprocess_pool.go", diff --git a/pkg/sentry/platform/systrap/shared_context.go b/pkg/sentry/platform/systrap/shared_context.go new file mode 100644 index 000000000..41ffb996b --- /dev/null +++ b/pkg/sentry/platform/systrap/shared_context.go @@ -0,0 +1,168 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package systrap + +import ( + "fmt" + "sync/atomic" + + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/sentry/platform" + "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" +) + +const ( + ackReset uint32 = 0 +) + +// sharedContext is an abstraction for interactions that the sentry has to +// perform with memory shared between it and the stub threads used for contexts. +// +// Any access to shared memory should most likely have a getter/setter through +// this struct. This is due to the following reasons: +// - The memory needs to be read or modified atomically because there is no +// (trusted) synchronization between the sentry and the stub processes. +// - Data read from shared memory may require validation before it can be used. +type sharedContext struct { + // subprocess is the subprocess that this sharedContext instance belongs to. + subprocess *subprocess + // contextID is the ID corresponding to the sysmsg.ThreadContext memory slot + // that is used for this sharedContext. + contextID uint64 + // shared is the handle to the shared memory that the sentry task goroutine + // reads from and writes to. + // NOTE: Using this handle directly without a getter from this function should + // most likely be avoided due to concerns listed above. + shared *sysmsg.ThreadContext +} + +func (s *subprocess) getSharedContext() (*sharedContext, error) { + s.mu.Lock() + defer s.mu.Unlock() + + id, ok := s.threadContextPool.Get() + if !ok { + return nil, fmt.Errorf("subprocess has too many active tasks (%d); failed to create a new one", maxGuestContexts) + } + s.IncRef() + sc := sharedContext{ + subprocess: s, + contextID: id, + shared: s.getThreadContextFromID(id), + } + sc.shared.Init(invalidThreadID) + + return &sc, nil +} + +func (sc *sharedContext) release() { + if sc == nil { + return + } + sc.subprocess.threadContextPool.Put(sc.contextID) + sc.subprocess.DecRef(sc.subprocess.release) +} + +func (sc *sharedContext) isActiveInSubprocess(s *subprocess) bool { + if sc == nil { + return false + } + return sc.subprocess == s +} + +// NotifyInterrupt implements interrupt.Receiver.NotifyInterrupt. +func (sc *sharedContext) NotifyInterrupt() { + // If this context is not being worked on right now we need to mark it as + // interrupted so the next executor does not start working on it. + atomic.StoreUint32(&sc.shared.Interrupt, 1) + if sc.threadID() == invalidThreadID { + return + } + sc.subprocess.sysmsgThreadsMu.Lock() + defer sc.subprocess.sysmsgThreadsMu.Unlock() + + threadID := atomic.LoadUint32(&sc.shared.ThreadID) + sysmsgThread, ok := sc.subprocess.sysmsgThreads[threadID] + if !ok { + // This is either an invalidThreadID or another garbage value; either way we + // don't know which thread to interrupt; best we can do is mark the context. + return + } + + t := sysmsgThread.thread + atomic.StoreUint64(&sysmsgThread.msg.InterruptedContextID, sc.contextID) + if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(t.tgid), uintptr(t.tid), uintptr(platform.SignalInterrupt)); e != 0 { + panic(fmt.Sprintf("failed to interrupt the child process %d: %v", t.tid, e)) + } +} + +func (sc *sharedContext) state() sysmsg.ContextState { + return sc.shared.State.Get() +} + +func (sc *sharedContext) setState(state sysmsg.ContextState) { + sc.shared.State.Set(state) +} + +func (sc *sharedContext) setInterrupt() { + atomic.StoreUint32(&sc.shared.Interrupt, 1) +} + +func (sc *sharedContext) clearInterrupt() { + atomic.StoreUint32(&sc.shared.Interrupt, 0) +} + +func (sc *sharedContext) setFPStateChanged() { + atomic.StoreUint64(&sc.shared.FPStateChanged, 1) +} + +func (sc *sharedContext) threadID() uint32 { + return atomic.LoadUint32(&sc.shared.ThreadID) +} + +func (sc *sharedContext) setThreadID(threadID uint32) { + if contextDecouplingExp { + panic("context decoupled systrap should never explicitly set ThreadID") + } + atomic.StoreUint32(&sc.shared.ThreadID, threadID) +} + +// EnableSentryFastPath indicates that the polling mode is enabled for the +// Sentry. It has to be called before putting the context into the context queue. +// This function is used if contextDecouplingExp=true because the fastpath +// is negotiated in ThreadContext. +func (sc *sharedContext) enableSentryFastPath() { + atomic.StoreUint32(&sc.shared.SentryFastPath, 1) +} + +// DisableSentryFastPath indicates that the polling mode for the sentry is +// disabled for the Sentry. +// This function is used if contextDecouplingExp=true because the fastpath +// is negotiated in ThreadContext. +func (sc *sharedContext) disableSentryFastPath() { + atomic.StoreUint32(&sc.shared.SentryFastPath, 0) +} + +func (sc *sharedContext) isAcked() bool { + return atomic.LoadUint32(&sc.shared.Acked) != ackReset +} + +func (sc *sharedContext) resetAcked() { + atomic.StoreUint32(&sc.shared.Acked, ackReset) +} + +func (sc *sharedContext) sleepOnState(state sysmsg.ContextState) { + sc.shared.SleepOnState(state) +} diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index ca3fa7244..b520b1eaa 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -680,9 +680,9 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool // Reset necessary registers. regs := &ac.StateData().Regs s.resetSysemuRegs(regs) - ctx := s.getThreadContextFromID(c.cid) - ctx.Regs = regs.PtraceRegs - restoreArchSpecificState(ctx, ac) + ctx := c.sharedContext + ctx.shared.Regs = regs.PtraceRegs + restoreArchSpecificState(ctx.shared, ac) // Get sysmsg thread bound to the context; no-op if contextDecoupling is on. sysThread, err := s.getSysmsgThread(regs, c, ac) @@ -691,36 +691,36 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool } // Check for interrupts, and ensure that future interrupts signal the context. - if !c.interrupt.Enable(c) { + if !c.interrupt.Enable(c.sharedContext) { // Pending interrupt; simulate. - ctx.Interrupt = 0 + ctx.clearInterrupt() c.signalInfo = linux.SignalInfo{Signo: int32(platform.SignalInterrupt)} return false, false, nil } defer c.interrupt.Disable() if contextDecouplingExp { - s.restoreFPState(nil, ctx, 0, c, ac) + restoreFPState(nil, ctx, 0, c, ac) // Place the context onto the context queue. - ctx.State.Set(sysmsg.ContextStateNone) - s.contextQueue.add(uint32(c.cid)) + ctx.setState(sysmsg.ContextStateNone) + s.contextQueue.add(uint32(ctx.contextID)) s.waitOnState(ctx) // Check if there's been an error. - tid := atomic.LoadUint32(&ctx.ThreadID) - if tid != invalidThreadID { - if sysThread, ok := s.sysmsgThreads[tid]; ok && sysThread.msg.Err != 0 { + threadID := ctx.threadID() + if threadID != invalidThreadID { + if sysThread, ok := s.sysmsgThreads[threadID]; ok && sysThread.msg.Err != 0 { msg := sysThread.msg panic(fmt.Sprintf("stub thread %d failed: err 0x%x line %d: %s", sysThread.thread.tid, msg.Err, msg.Line, msg)) } - log.Warningf("systrap: found unexpected ThreadContext.ThreadID field, expected %d found %d", invalidThreadID, tid) + log.Warningf("systrap: found unexpected ThreadContext.ThreadID field, expected %d found %d", invalidThreadID, threadID) } } else { msg := sysThread.msg t := sysThread.thread - s.restoreFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) + restoreFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) msg.EnableSentryFastPath() sysThread.waitEvent(sysmsg.ThreadStateDone) @@ -730,7 +730,7 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool panic(fmt.Sprintf("stub thread %d failed: err %d line %d: %s", t.tid, msg.Err, msg.Line, msg)) } - if ctx.State != sysmsg.ContextStateSyscallTrap { + if ctx.state() != sysmsg.ContextStateSyscallTrap { var err error sysThread.fpuStateToMsgOffset, err = msg.FPUStateOffset() if err != nil { @@ -738,27 +738,28 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool } } - retrieveArchSpecificState(ctx, ac) + retrieveArchSpecificState(ctx.shared, ac) } - regs.PtraceRegs = ctx.Regs + regs.PtraceRegs = ctx.shared.Regs // We have a signal. We verify however, that the signal was // either delivered from the kernel or from this process. We // don't respect other signals. - c.signalInfo = ctx.SignalInfo - if ctx.State == sysmsg.ContextStateSyscallCanBePatched { - ctx.State = sysmsg.ContextStateSyscall + c.signalInfo = ctx.shared.SignalInfo + ctxState := ctx.state() + if ctxState == sysmsg.ContextStateSyscallCanBePatched { + ctxState = sysmsg.ContextStateSyscall shouldPatchSyscall = true } - if ctx.State == sysmsg.ContextStateSyscall || ctx.State == sysmsg.ContextStateSyscallTrap { + if ctxState == sysmsg.ContextStateSyscall || ctxState == sysmsg.ContextStateSyscallTrap { if maybePatchSignalInfo(regs, &c.signalInfo) { return false, false, nil } updateSyscallRegs(regs) return true, shouldPatchSyscall, nil - } else if ctx.State != sysmsg.ContextStateFault { - panic(fmt.Sprintf("unknown context state: %v", ctx.State)) + } else if ctxState != sysmsg.ContextStateFault { + panic(fmt.Sprintf("unknown context state: %v", ctxState)) } return false, false, nil @@ -773,23 +774,21 @@ const ( threadKickTimeout = uint64(20000) ) -func (s *subprocess) waitOnState(ctx *sysmsg.ThreadContext) { - // ackedEvents is always reset to 0 at the end of this function. - ackedEvents := uint32(0) +func (s *subprocess) waitOnState(ctx *sharedContext) { kicked := false slowPath := false start := cputicks() handshake := false - for curState := ctx.State.Get(); curState == sysmsg.ContextStateNone; curState = ctx.State.Get() { + for curState := ctx.state(); curState == sysmsg.ContextStateNone; curState = ctx.state() { if !slowPath { delta := uint64(cputicks() - start) if delta > decoupledDeepSleepTimeout { - ctx.DisableSentryFastPath() + ctx.disableSentryFastPath() slowPath = true continue } - if !handshake && ackedEvents != atomic.LoadUint32(&ctx.Acked) { + if !handshake && ctx.isAcked() { handshake = true continue } @@ -802,12 +801,12 @@ func (s *subprocess) waitOnState(ctx *sysmsg.ThreadContext) { s.kickSysmsgThread() } - ctx.SleepOnState(curState) + ctx.sleepOnState(curState) } } - atomic.StoreUint32(&ctx.Acked, 0) - ctx.EnableSentryFastPath() + ctx.resetAcked() + ctx.enableSentryFastPath() } func (s *subprocess) kickSysmsgThread() { @@ -894,18 +893,17 @@ func (s *subprocess) Unmap(addr hostarch.Addr, length uint64) { } func (s *subprocess) PullFullState(c *context, ac *arch.Context64) error { - if s != c.subprocess { + if !c.sharedContext.isActiveInSubprocess(s) { panic("Attempted to PullFullState for context that is not used in subprocess") } - ctx := s.getThreadContextFromID(c.cid) if contextDecouplingExp { - s.saveFPState(nil, ctx, 0, c, ac) + saveFPState(nil, c.sharedContext, 0, c, ac) } else { sysThread, err := s.getSysmsgThread(&ac.StateData().Regs, c, ac) if err != nil { return err } - s.saveFPState(sysThread.msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) + saveFPState(sysThread.msg, c.sharedContext, sysThread.fpuStateToMsgOffset, c, ac) } return nil } @@ -1023,8 +1021,8 @@ func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *a if contextDecouplingExp { sysThread.msg.ContextID = uint64(invalidContextID) } else { - s.getThreadContextFromID(c.cid).ThreadID = threadID - sysThread.msg.ContextID = c.cid + c.sharedContext.setThreadID(threadID) + sysThread.msg.ContextID = c.sharedContext.contextID } sysThread.msg.Self = uint64(sysmsgStackAddr + sysmsg.MsgOffsetFromSharedStack) sysThread.msg.SyshandlerStack = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr())) @@ -1100,60 +1098,19 @@ func (s *subprocess) PostFork() { s.usertrap.PostFork() // +checklocksforce: PreFork acquires, above. } -// registerContext registers the context to an ID specific to this subprocess. -// It will return an error if too many contexts are already active in this -// subprocess. -func (s *subprocess) registerContext(c *context) error { - s.mu.Lock() - c.mu.Lock() - // Unlock manually for the sake of not holding the lock while initializing - // context memory. - locked := true - unlock := func() { - if locked { - c.mu.Unlock() - s.mu.Unlock() - locked = false +// activateContext activates the context in this subprocess. +// No-op if the context is already active within the subprocess; if not, +// deactivates it from its last subprocess. +func (s *subprocess) activateContext(c *context) error { + if !c.sharedContext.isActiveInSubprocess(s) { + c.sharedContext.release() + c.sharedContext = nil + + shared, err := s.getSharedContext() + if err != nil { + return err } + c.sharedContext = shared } - defer unlock() - - if s == c.subprocess && c.cid != invalidContextID { - return nil - } - - id, ok := s.threadContextPool.Get() - if !ok { - return fmt.Errorf("subprocess has too many active threads (%d); failed to create a new one", maxGuestContexts) - } - s.IncRef() - c.cid = id - c.subprocess = s - c.FullStateChanged() - unlock() - - threadContext := s.getThreadContextFromID(id) - threadContext.Init(invalidThreadID) return nil } - -// unregisterContext releases all references held for this context. -// -// Precondition: context c must have been active within subprocess s. -func (s *subprocess) unregisterContext(c *context) { - if s == nil { - return - } - c.mu.Lock() - cid := c.cid - c.cid = invalidContextID - c.subprocess = nil - c.mu.Unlock() - - s.mu.Lock() - delete(s.faultedContexts, c) - s.threadContextPool.Put(cid) - s.mu.Unlock() - - s.DecRef(s.release) -} diff --git a/pkg/sentry/platform/systrap/subprocess_amd64_unsafe.go b/pkg/sentry/platform/systrap/subprocess_amd64_unsafe.go deleted file mode 100644 index b0d4cff4b..000000000 --- a/pkg/sentry/platform/systrap/subprocess_amd64_unsafe.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build amd64 -// +build amd64 - -package systrap - -import ( - "unsafe" - - "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" -) - -//go:nosplit -func isFPStateInContextRegion(ctx *sysmsg.ThreadContext) bool { - // If context decoupling experiment is ON then both the sighandler and - // syshandler save FPState to the context region since contexts will move - // threads. Otherwise only syshandler will save FPState to the region. - return contextDecouplingExp || ctx.State == sysmsg.ContextStateSyscallTrap -} - -func (s *subprocess) saveFPState(msg *sysmsg.Msg, ctx *sysmsg.ThreadContext, fpuToMsgOffset uint64, c *context, ac *arch.Context64) { - fpState := ac.FloatingPointData().BytePointer() - dst := unsafeSlice(uintptr(unsafe.Pointer(fpState)), c.fpLen) - var src []byte - if isFPStateInContextRegion(ctx) { - src = ctx.FPState[:] - } else { - src = unsafeSlice(uintptr(unsafe.Pointer(msg))+uintptr(fpuToMsgOffset), c.fpLen) - } - copy(dst, src) -} - -// restoreFPStateDecoupledContext writes FPState from c to the thread context -// shared memory region if there is any need to do so. -func (s *subprocess) restoreFPState(msg *sysmsg.Msg, ctx *sysmsg.ThreadContext, fpuToMsgOffset uint64, c *context, ac *arch.Context64) { - if !c.needRestoreFPState { - return - } - c.needRestoreFPState = false - ctx.FPStateChanged = 1 - - fpState := ac.FloatingPointData().BytePointer() - src := unsafeSlice(uintptr(unsafe.Pointer(fpState)), c.fpLen) - var dst []byte - if isFPStateInContextRegion(ctx) { - dst = ctx.FPState[:] - } else { - dst = unsafeSlice(uintptr(unsafe.Pointer(msg))+uintptr(fpuToMsgOffset), c.fpLen) - } - copy(dst, src) -} diff --git a/pkg/sentry/platform/systrap/subprocess_arm64_unsafe.go b/pkg/sentry/platform/systrap/subprocess_arm64_unsafe.go deleted file mode 100644 index 1584be21d..000000000 --- a/pkg/sentry/platform/systrap/subprocess_arm64_unsafe.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build arm64 -// +build arm64 - -package systrap - -import ( - "unsafe" - - "gvisor.dev/gvisor/pkg/sentry/arch" - "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" -) - -//go:nosplit -func isFPStateInContextRegion(ctx *sysmsg.ThreadContext) bool { - // If context decoupling experiment is ON then both the sighandler and - // syshandler save FPState to the context region since contexts will move - // threads. Otherwise only syshandler will save FPState to the region. - return contextDecouplingExp || ctx.State == sysmsg.ContextStateSyscallTrap -} - -func (s *subprocess) restoreFPState(msg *sysmsg.Msg, ctx *sysmsg.ThreadContext, fpuToMsgOffset uint64, c *context, ac *arch.Context64) { - // c.needRestoreFPState is changed only from the task goroutine, so it can - // be accessed without locks. - if !c.needRestoreFPState { - return - } - c.needRestoreFPState = false - ctx.FPStateChanged = 1 - - fpState := ac.FloatingPointData().BytePointer() - src := unsafeSlice(uintptr(unsafe.Pointer(fpState)), c.fpLen) - var dst []byte - if isFPStateInContextRegion(ctx) { - dst = ctx.FPState[:] - } else { - dst = unsafeSlice(uintptr(unsafe.Pointer(msg))+uintptr(fpuToMsgOffset), c.fpLen) - } - copy(dst, src) -} - -func (s *subprocess) saveFPState(msg *sysmsg.Msg, ctx *sysmsg.ThreadContext, fpuToMsgOffset uint64, c *context, ac *arch.Context64) { - fpState := ac.FloatingPointData().BytePointer() - dst := unsafeSlice(uintptr(unsafe.Pointer(fpState)), c.fpLen) - var src []byte - if isFPStateInContextRegion(ctx) { - src = ctx.FPState[:] - } else { - src = unsafeSlice(uintptr(unsafe.Pointer(msg))+uintptr(fpuToMsgOffset), c.fpLen) - } - copy(dst, src) -} diff --git a/pkg/sentry/platform/systrap/subprocess_unsafe.go b/pkg/sentry/platform/systrap/subprocess_unsafe.go index fadc0a3f8..3f7a3d44e 100644 --- a/pkg/sentry/platform/systrap/subprocess_unsafe.go +++ b/pkg/sentry/platform/systrap/subprocess_unsafe.go @@ -26,6 +26,7 @@ import ( "unsafe" "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/memmap" "gvisor.dev/gvisor/pkg/sentry/pgalloc" "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" @@ -68,3 +69,43 @@ func mmapContextQueueForSentry(memoryFile *pgalloc.MemoryFile, opts pgalloc.Allo return fr, (*contextQueue)(unsafe.Pointer(addr)) } + +//go:nosplit +func isFPStateInContextRegion(ctx *sharedContext) bool { + // If context decoupling experiment is ON then both the sighandler and + // syshandler save FPState to the context region since contexts will move + // threads. Otherwise only syshandler will save FPState to the region. + return contextDecouplingExp || ctx.state() == sysmsg.ContextStateSyscallTrap +} + +func saveFPState(msg *sysmsg.Msg, ctx *sharedContext, fpuToMsgOffset uint64, c *context, ac *arch.Context64) { + fpState := ac.FloatingPointData().BytePointer() + dst := unsafeSlice(uintptr(unsafe.Pointer(fpState)), archState.FpLen()) + var src []byte + if isFPStateInContextRegion(ctx) { + src = ctx.shared.FPState[:] + } else { + src = unsafeSlice(uintptr(unsafe.Pointer(msg))+uintptr(fpuToMsgOffset), archState.FpLen()) + } + copy(dst, src) +} + +// restoreFPStateDecoupledContext writes FPState from c to the thread context +// shared memory region if there is any need to do so. +func restoreFPState(msg *sysmsg.Msg, ctx *sharedContext, fpuToMsgOffset uint64, c *context, ac *arch.Context64) { + if !c.needRestoreFPState { + return + } + c.needRestoreFPState = false + ctx.setFPStateChanged() + + fpState := ac.FloatingPointData().BytePointer() + src := unsafeSlice(uintptr(unsafe.Pointer(fpState)), archState.FpLen()) + var dst []byte + if isFPStateInContextRegion(ctx) { + dst = ctx.shared.FPState[:] + } else { + dst = unsafeSlice(uintptr(unsafe.Pointer(msg))+uintptr(fpuToMsgOffset), archState.FpLen()) + } + copy(dst, src) +} diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go index 8a2c5b9a2..7448a1232 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go @@ -337,22 +337,6 @@ func (m *Msg) DisableSentryFastPath() { atomic.StoreUint32(&m.sentryFastPath, 0) } -// EnableSentryFastPath indicates that the polling mode is enabled for the -// Sentry. It has to be called before putting the context into the context queue. -// This function is used if contextDecouplingExp=true because the fastpath -// is negotiated in ThreadContext -func (c *ThreadContext) EnableSentryFastPath() { - c.SentryFastPath = 1 -} - -// DisableSentryFastPath indicates that the polling mode for the sentry is -// disabled for the Sentry. -// This function is used if contextDecouplingExp=true because the fastpath -// is negotiated in ThreadContext. -func (c *ThreadContext) DisableSentryFastPath() { - atomic.StoreUint32(&c.SentryFastPath, 0) -} - // FPUStateOffset returns the offset of a saved FPU state to the msg. func (m *Msg) FPUStateOffset() (uint64, error) { offset := m.fpState diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_amd64.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg_amd64.go index 03a604953..87ad74ed0 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_amd64.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_amd64.go @@ -63,6 +63,11 @@ func (s *ArchState) Init() { } } +// FpLen returns the FP state length for AMD64. +func (s *ArchState) FpLen() int { + return int(s.fpLen) +} + func (s *ArchState) String() string { var b strings.Builder fmt.Fprintf(&b, "sysmsg.ArchState{") diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_arm64.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg_arm64.go index ae5f3e4d3..b84866986 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_arm64.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_arm64.go @@ -43,6 +43,11 @@ func (s *ArchState) Init() { s.fpLen = uint32(fpLenUint) } +// FpLen returns the FP state length for ARM. +func (s *ArchState) FpLen() int { + return int(s.fpLen) +} + func (s *ArchState) String() string { var b strings.Builder fmt.Fprintf(&b, "sysmsg.ArchState{") diff --git a/pkg/sentry/platform/systrap/systrap.go b/pkg/sentry/platform/systrap/systrap.go index 64e919bd0..eea34b3cd 100644 --- a/pkg/sentry/platform/systrap/systrap.go +++ b/pkg/sentry/platform/systrap/systrap.go @@ -52,18 +52,16 @@ import ( "fmt" "os" "sync" - "sync/atomic" - "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" pkgcontext "gvisor.dev/gvisor/pkg/context" - "gvisor.dev/gvisor/pkg/cpuid" "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/memutil" "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/pgalloc" "gvisor.dev/gvisor/pkg/sentry/platform" "gvisor.dev/gvisor/pkg/sentry/platform/interrupt" + "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" "gvisor.dev/gvisor/pkg/sentry/platform/systrap/usertrap" ) @@ -100,6 +98,9 @@ var ( // stubInitialized controls one-time stub initialization. stubInitialized sync.Once + + // archState stores architecture-specific details used in the platform. + archState sysmsg.ArchState ) // context is an implementation of the platform context. @@ -110,16 +111,15 @@ type context struct { // interrupt is the interrupt context. interrupt interrupt.Forwarder + // sharedContext is everything related to this context that is resident in + // shared memory with the stub thread. + // sharedContext is only accessed on the Task goroutine, therefore it is not + // mutex protected. + sharedContext *sharedContext + // mu protects the following fields. mu sync.Mutex - // subprocess is the current subprocess used to execute the context. - subprocess *subprocess - - // cid is the ID of the context in the address space of the current - // subprocess used to run it. - cid uint64 - // If lastFaultSP is non-nil, the last context switch was due to a fault // received while executing lastFaultSP. Only context.Switch may set // lastFaultSP to a non-nil value. @@ -137,9 +137,6 @@ type context struct { // application code. (Note: Unused if contextDecouplingExp=true). sysmsgThread *sysmsgThread - // fpLen is the size of the floating point context. - fpLen int - // needRestoreFPState indicates that the FPU state has been changed by // the Sentry and has to be updated on the stub thread. needRestoreFPState bool @@ -174,13 +171,10 @@ func (c *context) Switch(ctx pkgcontext.Context, mm platform.MemoryManager, ac * as := mm.AddressSpace() s := as.(*subprocess) - - if s != c.subprocess { - c.subprocess.unregisterContext(c) - if err := s.registerContext(c); err != nil { - return nil, hostarch.NoAccess, err - } + if err := s.activateContext(c); err != nil { + return nil, hostarch.NoAccess, err } + restart: isSyscall, needPatch, err := s.switchToApp(c, ac) if err != nil { @@ -281,52 +275,15 @@ func (c *context) Interrupt() { c.interrupt.NotifyInterrupt() } -// NotifyInterrupt implements interrupt.Receiver.NotifyInterrupt. -// -// Another reasonable existing object to implement NotifyInterrupt would be -// sysmsg.ThreadContext, because we can write the correct host TID into it -// to know which thread to send the signal to. However, because it is in shared -// memory, one subprocess can overwrite it to have the sentry send an interrupt -// to a completely different subprocess. -// For this reason we use systrap.context and check that the target thread -// is actually valid within the subprocess. -func (c *context) NotifyInterrupt() { - c.mu.Lock() - s := c.subprocess - cid := c.cid - c.mu.Unlock() - - if s == nil || cid == invalidContextID { - return - } - - threadContext := s.getThreadContextFromID(cid) - atomic.StoreUint32(&threadContext.Interrupt, 1) - threadID := atomic.LoadUint32(&threadContext.ThreadID) - - s.sysmsgThreadsMu.Lock() - defer s.sysmsgThreadsMu.Unlock() - - sysmsgThread, ok := s.sysmsgThreads[threadID] - if !ok { - // This is either an invalidThreadID or another garbage value; either way we - // don't know which thread to interrupt; best we can do is mark the context. - return - } - - t := sysmsgThread.thread - atomic.StoreUint64(&sysmsgThread.msg.InterruptedContextID, cid) - if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(t.tgid), uintptr(t.tid), uintptr(platform.SignalInterrupt)); e != 0 { - panic(fmt.Sprintf("failed to interrupt the child process %d: %v", t.tid, e)) - } -} - // Release releases all platform resources used by the context. func (c *context) Release() { if c.sysmsgThread != nil { c.sysmsgThread.destroy() } - c.subprocess.unregisterContext(c) + if c.sharedContext != nil { + c.sharedContext.release() + c.sharedContext = nil + } } // PrepareSleep implements platform.Context.platform.PrepareSleep. @@ -356,6 +313,9 @@ func (*Systrap) MinUserAddress() hostarch.Addr { // New returns a new seccomp-based implementation of the platform interface. func New() (*Systrap, error) { + // CPUID information has been initialized at this point. + archState.Init() + mf, err := createMemoryFile() if err != nil { return nil, err @@ -412,11 +372,7 @@ func (p *Systrap) NewAddressSpace(any) (platform.AddressSpace, <-chan struct{}, // NewContext returns an interruptible context. func (*Systrap) NewContext(ctx pkgcontext.Context) platform.Context { - fs := cpuid.FromContext(ctx) - fpLen, _ := fs.ExtendedStateSize() return &context{ - cid: invalidContextID, - fpLen: int(fpLen), needRestoreFPState: true, needToPullFullState: false, } From d3cc1c4136eef7271725ab55fefed5655f6839c6 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Fri, 17 Mar 2023 07:57:39 -0700 Subject: [PATCH 21/49] Internal change. PiperOrigin-RevId: 517413073 --- website/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/BUILD b/website/BUILD index 41c3e7cc9..dffc6c378 100644 --- a/website/BUILD +++ b/website/BUILD @@ -112,8 +112,8 @@ genrule( pkg_tar( name = "config", srcs = [ - ":css", "_config.yml", + ":css", "//website/blog:index.html", ] + glob([ "assets/**", From 9b1705598c3a5362c875924ce99f100384bbc0e8 Mon Sep 17 00:00:00 2001 From: Ivan Prisyazhnyy Date: Wed, 15 Mar 2023 17:01:00 +0100 Subject: [PATCH 22/49] runsc/restore: must be able to restore to prepared container runsc restore must be able to restore into a prepared container to allow the user to configure container specific namespaces as otherwise there is no other means to override different bundle parameters and especially namespaces configuration. Specifically, otherwise, it is impossible to configure networking other than by cloning the bundle config and preparing special netns for the container instead of just configuring it on the fly without whole management complexity. This fix allows to do the following: - runsc create - configure container netns (even with the netlink api) - restore into the container with nice networking It also fixes the restore command to behave exactly as it is stated in the documentation: runsc create runsc restore --image-path= at https://github.com/google/gvisor/blob/master/g3doc/user_guide/checkpoint_restore.md Generally, user shall not try to do a checkpoint restore into the void. If container is not found, we keep back compatibility, but print a warning. Signed-off-by: Ivan Prisyazhnyy --- runsc/cmd/BUILD | 1 + runsc/cmd/restore.go | 65 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 96e81a4ae..e0f5a015d 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -53,6 +53,7 @@ go_library( deps = [ "//pkg/abi/linux", "//pkg/atomicbitops", + "//pkg/cleanup", "//pkg/coretag", "//pkg/coverage", "//pkg/cpuid", diff --git a/runsc/cmd/restore.go b/runsc/cmd/restore.go index a58c3f865..604523c4e 100644 --- a/runsc/cmd/restore.go +++ b/runsc/cmd/restore.go @@ -16,10 +16,13 @@ package cmd import ( "context" + "os" "path/filepath" "github.com/google/subcommands" "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/cleanup" + "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/runsc/cmd/util" "gvisor.dev/gvisor/runsc/config" "gvisor.dev/gvisor/runsc/container" @@ -89,32 +92,76 @@ func (r *Restore) Execute(_ context.Context, f *flag.FlagSet, args ...any) subco if bundleDir == "" { bundleDir = getwdOrDie() } - spec, err := specutils.ReadSpec(bundleDir, conf) - if err != nil { - return util.Errorf("reading spec: %v", err) - } - specutils.LogSpecDebug(spec, conf.OCISeccomp) - if r.imagePath == "" { return util.Errorf("image-path flag must be provided") } + var cu cleanup.Cleanup + defer cu.Clean() + conf.RestoreFile = filepath.Join(r.imagePath, checkpointFileName) runArgs := container.Args{ ID: id, - Spec: spec, + Spec: nil, BundleDir: bundleDir, ConsoleSocket: r.consoleSocket, PIDFile: r.pidFile, UserLog: r.userLog, Attached: !r.detach, } - ws, err := container.Run(conf, runArgs) + + log.Debugf("Restore container, cid: %s, rootDir: %q", id, conf.RootDir) + c, err := container.Load(conf.RootDir, container.FullID{ContainerID: id}, container.LoadOpts{}) if err != nil { - return util.Errorf("running container: %v", err) + if err != os.ErrNotExist { + return util.Errorf("loading container: %v", err) + } + + log.Warningf("Container not found, creating new one, cid: %s, spec from: %s", id, bundleDir) + + // Read the spec again here to ensure flag annotations from the spec are + // applied to "conf". + if runArgs.Spec, err = specutils.ReadSpec(bundleDir, conf); err != nil { + return util.Errorf("reading spec: %v", err) + } + specutils.LogSpecDebug(runArgs.Spec, conf.OCISeccomp) + + if c, err = container.New(conf, runArgs); err != nil { + return util.Errorf("creating container: %v", err) + } + + // Clean up partially created container if an error occurs. + // Any errors returned by Destroy() itself are ignored. + cu.Add(func() { + c.Destroy() + }) + } else { + runArgs.Spec = c.Spec + } + + log.Debugf("Restore: %v", conf.RestoreFile) + if err := c.Restore(runArgs.Spec, conf, conf.RestoreFile); err != nil { + return util.Errorf("starting container: %v", err) + } + + // If we allocate a terminal, forward signals to the sandbox process. + // Otherwise, Ctrl+C will terminate this process and its children, + // including the terminal. + if c.Spec.Process.Terminal { + stopForwarding := c.ForwardSignals(0, true /* fgProcess */) + defer stopForwarding() + } + + var ws unix.WaitStatus + if runArgs.Attached { + if ws, err = c.Wait(); err != nil { + return util.Errorf("running container: %v", err) + } } *waitStatus = ws + cu.Release() + return subcommands.ExitSuccess } From edd7fd2e6022e209c10d40eb594fc0ffb22b8bab Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Fri, 17 Mar 2023 12:10:50 -0700 Subject: [PATCH 23/49] gro: fix bug where handshake packets would be stuck waiting in GRO GRO would not immediately flush the final ACK in the SYN-SYN/ACK-ACK handshake. This could lead to a situation where - Client A calls connect(), which returns once the final ACK of the handshake is sent and A reaches state ESTABLISHED - The ACK gets GRO'd, delaying it from reaching the server - Client B calls non-blocking connect() - Client B's ACK gets GRO'd as well - Client B is marked as ESTABLISHED - The server, with accept queue size 1, is only going to accept one connection, but two clients are ESTABLISHED. It now immediately flushes packets with no payload, as they are important to TCP connection state and management. PiperOrigin-RevId: 517474519 --- pkg/tcpip/stack/gro.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/tcpip/stack/gro.go b/pkg/tcpip/stack/gro.go index a3b708fd5..29a1b7061 100644 --- a/pkg/tcpip/stack/gro.go +++ b/pkg/tcpip/stack/gro.go @@ -226,6 +226,8 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo // Flush groPkt or merge the packets. pktSize := pkt.Data().Size() flags := tcpHdr.Flags() + dataOff := tcpHdr.DataOffset() + tcpPayloadSize := pkt.Data().Size() - len(ipHdr) - int(dataOff) if flushGROPkt { // Flush the existing GRO packet. Don't hold bucket.mu while // processing the packet. @@ -239,12 +241,10 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo } else if groPkt != nil { // Merge pkt in to GRO packet. buf := pkt.Data().ToBuffer() - dataOff := tcpHdr.DataOffset() buf.TrimFront(int64(len(ipHdr)) + int64(dataOff)) groPkt.pkt.Data().MergeBuffer(&buf) buf.Release() // Update the IP total length. - tcpPayloadSize := pkt.Data().Size() - len(ipHdr) - int(dataOff) updateIPHdr(groPkt.ipHdr, tcpPayloadSize) // Add flags from the packet to the GRO packet. groPkt.tcpHdr.SetFlags(uint8(groPkt.tcpHdr.Flags() | (flags & (header.TCPFlagFin | header.TCPFlagPsh)))) @@ -261,6 +261,7 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo // malformed, a local GSO packet, or has already been handled by host // GRO. flush := header.TCPFlags(flags)&(header.TCPFlagUrg|header.TCPFlagPsh|header.TCPFlagRst|header.TCPFlagSyn|header.TCPFlagFin) != 0 + flush = flush || tcpPayloadSize == 0 if groPkt != nil { flush = flush || pktSize != groPkt.initialLength } From acf460d0d7350384817d2748fe02c11b8a701e8e Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Fri, 17 Mar 2023 16:52:01 -0700 Subject: [PATCH 24/49] Avoid using AT_EMPTY_PATH while making linkat(2) host syscall. Using linkat(targetFD, "", newdirfd, name, AT_EMPTY_PATH) requires CAP_DAC_READ_SEARCH in the *root* userns. See fs/namei.c:do_linkat(). It checks `capable(CAP_DAC_READ_SEARCH)` which actually performs the capability check in the root userns. The gofer and the sandbox process may be configured to be running in a non-root userns. Then the linkat(2) syscall fails with ENOENT. We are forced to use the target's parent directory FD to perform the linkat(2) operation. Fixes #8688 PiperOrigin-RevId: 517540797 --- pkg/sentry/fsimpl/gofer/dentry_impl.go | 4 ++- pkg/sentry/fsimpl/gofer/directfs_dentry.go | 10 ++++++- runsc/boot/filter/config.go | 2 +- runsc/fsgofer/lisafs.go | 33 ++++++++++++++++------ test/syscalls/BUILD | 4 ++- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/pkg/sentry/fsimpl/gofer/dentry_impl.go b/pkg/sentry/fsimpl/gofer/dentry_impl.go index c6bceab20..8cc74dd74 100644 --- a/pkg/sentry/fsimpl/gofer/dentry_impl.go +++ b/pkg/sentry/fsimpl/gofer/dentry_impl.go @@ -325,7 +325,9 @@ func (d *dentry) mknod(ctx context.Context, name string, creds *auth.Credentials } } -// Precondition: !d.isSynthetic(). +// Preconditions: +// - !d.isSynthetic(). +// - d.fs.renameMu must be locked. func (d *dentry) link(ctx context.Context, target *dentry, name string) (*dentry, error) { switch dt := d.impl.(type) { case *lisafsDentry: diff --git a/pkg/sentry/fsimpl/gofer/directfs_dentry.go b/pkg/sentry/fsimpl/gofer/directfs_dentry.go index 232c872fd..fdbfee371 100644 --- a/pkg/sentry/fsimpl/gofer/directfs_dentry.go +++ b/pkg/sentry/fsimpl/gofer/directfs_dentry.go @@ -512,8 +512,16 @@ func (d *directfsDentry) bindAt(ctx context.Context, name string, creds *auth.Cr return child, nil } +// Precondition: d.fs.renameMu must be locked. func (d *directfsDentry) link(target *directfsDentry, name string) (*dentry, error) { - if err := unix.Linkat(target.controlFD, "", d.controlFD, name, unix.AT_EMPTY_PATH); err != nil { + // Using linkat(targetFD, "", newdirfd, name, AT_EMPTY_PATH) requires + // CAP_DAC_READ_SEARCH in the *root* userns. With directfs, the sandbox + // process has CAP_DAC_READ_SEARCH in its own userns. But the sandbox is + // running in a different userns. So we can't use AT_EMPTY_PATH. Fallback to + // using olddirfd to call linkat(2). + // Also note that d and target are from the same mount. Given target is a + // non-directory and d is a directory, target.parent must exist. + if err := unix.Linkat(target.parent.impl.(*directfsDentry).controlFD, target.name, d.controlFD, name, 0); err != nil { return nil, err } // Note that we don't need to set uid/gid for the new child. This is a hard diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go index 30a038a63..e138ef5b0 100644 --- a/runsc/boot/filter/config.go +++ b/runsc/boot/filter/config.go @@ -426,7 +426,7 @@ func hostFilesystemFilters() seccomp.SyscallRules { seccomp.MatchAny{}, validFDCheck, seccomp.MatchAny{}, - seccomp.EqualTo(unix.AT_EMPTY_PATH), + seccomp.EqualTo(0), }, }, unix.SYS_MKDIRAT: []seccomp.Rule{ diff --git a/runsc/fsgofer/lisafs.go b/runsc/fsgofer/lisafs.go index c78e34013..dca243cc1 100644 --- a/runsc/fsgofer/lisafs.go +++ b/runsc/fsgofer/lisafs.go @@ -248,6 +248,16 @@ func (fd *controlFDLisa) getWritableFD() (int, error) { return writableFD, nil } +func (fd *controlFDLisa) getParentFD() (int, string, error) { + filePath := fd.Node().FilePath() + if filePath == "/" { + log.Warningf("getParentFD() call on the root") + return -1, "", unix.EINVAL + } + parent, err := unix.Open(path.Dir(filePath), openFlags|unix.O_PATH, 0) + return parent, path.Base(filePath), err +} + // FD implements lisafs.ControlFDImpl.FD. func (fd *controlFDLisa) FD() *lisafs.ControlFD { if fd == nil { @@ -280,15 +290,14 @@ func (fd *controlFDLisa) SetStat(stat lisafs.SetStatReq) (failureMask uint32, fa if fd.IsSocket() { // fchmod(2) on socket files created via bind(2) fails. We need to // fchmodat(2) it from its parent. - sockPath := fd.Node().FilePath() - parent, err := unix.Open(path.Dir(sockPath), openFlags|unix.O_PATH, 0) + parent, sockName, err := fd.getParentFD() if err == nil { // Note that AT_SYMLINK_NOFOLLOW flag is not currently supported. - err = unix.Fchmodat(parent, path.Base(sockPath), stat.Mode&^unix.S_IFMT, 0 /* flags */) + err = unix.Fchmodat(parent, sockName, stat.Mode&^unix.S_IFMT, 0 /* flags */) unix.Close(parent) } if err != nil { - log.Warningf("SetStat fchmod failed on socket %q, err: %v", sockPath, err) + log.Warningf("SetStat fchmod failed on socket %q, err: %v", fd.Node().FilePath(), err) failureMask |= unix.STATX_MODE failureErr = err } @@ -332,10 +341,9 @@ func (fd *controlFDLisa) SetStat(stat lisafs.SetStatReq) (failureMask uint32, fa // utimensat operates different that other syscalls. To operate on a // symlink it *requires* AT_SYMLINK_NOFOLLOW with dirFD and a non-empty // name. We need the parent FD. - symlinkPath := fd.Node().FilePath() - parent, err := unix.Open(path.Dir(symlinkPath), openFlags|unix.O_PATH, 0) + parent, symlinkName, err := fd.getParentFD() if err == nil { - err = fsutil.Utimensat(parent, path.Base(symlinkPath), utimes, unix.AT_SYMLINK_NOFOLLOW) + err = fsutil.Utimensat(parent, symlinkName, utimes, unix.AT_SYMLINK_NOFOLLOW) unix.Close(parent) } if err != nil { @@ -669,8 +677,17 @@ func (fd *controlFDLisa) Symlink(name string, target string, uid lisafs.UID, gid // Link implements lisafs.ControlFDImpl.Link. func (fd *controlFDLisa) Link(dir lisafs.ControlFDImpl, name string) (*lisafs.ControlFD, linux.Statx, error) { + // Using linkat(targetFD, "", newdirfd, name, AT_EMPTY_PATH) requires + // CAP_DAC_READ_SEARCH in the *root* userns. The gofer process has + // CAP_DAC_READ_SEARCH in its own userns. But sometimes the gofer may be + // running in a different userns. So we can't use AT_EMPTY_PATH. Fallback + // to using olddirfd to call linkat(2). + oldDirFD, oldName, err := fd.getParentFD() + if err != nil { + return nil, linux.Statx{}, err + } dirFD := dir.(*controlFDLisa) - if err := unix.Linkat(fd.hostFD, "", dirFD.hostFD, name, unix.AT_EMPTY_PATH); err != nil { + if err := unix.Linkat(oldDirFD, oldName, dirFD.hostFD, name, 0); err != nil { return nil, linux.Statx{}, err } cu := cleanup.Make(func() { diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index 530afc4f8..91dd42d48 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -324,7 +324,9 @@ syscall_test( add_fusefs = True, add_overlay = True, test = "//test/syscalls/linux:link_test", - use_tmpfs = True, # gofer needs CAP_DAC_READ_SEARCH to use AT_EMPTY_PATH with linkat(2) + # TODO(gvisor.dev/issue/6739): Remove use_tmpfs=True once gofer filesystem + # supports hard links correctly. + use_tmpfs = True, ) syscall_test( From 57231c71ec5da6c65a01e4f9897cf49925614b77 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Mon, 20 Mar 2023 11:08:39 -0700 Subject: [PATCH 25/49] python: exclude flaky test_control_and_wait The test uses poll() incorrectly and flakes. Attempting to fix upstream: https://github.com/python/cpython/issues/102795 PiperOrigin-RevId: 518021055 --- test/runtimes/proctor/lib/python.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/runtimes/proctor/lib/python.go b/test/runtimes/proctor/lib/python.go index 985420597..0684331c9 100644 --- a/test/runtimes/proctor/lib/python.go +++ b/test/runtimes/proctor/lib/python.go @@ -120,6 +120,9 @@ var exclude = map[string][]string{ "UDPLITETimeoutTest.testTimeoutZero", "UDPLITETimeoutTest.testUDPLITETimeout", }, + // TODO(b/274167897): Un-exclude test cases once this is patched upstream. + // The test is broken: https://github.com/python/cpython/issues/102795 + "test_epoll": []string{"TestEPoll.test_control_and_wait"}, } // Some python test libraries contain other test libraries that have test cases From 5dde242a839647c8f615e51bf58f0a58009fa71f Mon Sep 17 00:00:00 2001 From: Alex Konradi Date: Mon, 20 Mar 2023 14:19:52 -0700 Subject: [PATCH 26/49] Add test for poll(POLLIN) on TCP around accept Add a test that asserts that POLLIN is set on a TCP listener when a new connection is available to be accepted and is unset after the connection is actually accepted. PiperOrigin-RevId: 518075095 --- test/syscalls/linux/tcp_socket.cc | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 59549dddc..932f4d254 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -994,6 +994,55 @@ TEST_P(TcpSocketTest, PollAfterShutdown) { SyscallSucceedsWithValue(1)); } +TEST_P(SimpleTcpSocketTest, PollAroundAccept) { + const FileDescriptor listener = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + sockaddr_storage addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + socklen_t addrlen = sizeof(addr); + + // Bind to some port. + ASSERT_THAT(bind(listener.get(), AsSockAddr(&addr), addrlen), + SyscallSucceeds()); + ASSERT_THAT(listen(listener.get(), SOMAXCONN), SyscallSucceeds()); + + // Get the address we're bound to. We need to do this because we're allowing + // the stack to pick a port for us. + ASSERT_THAT(getsockname(listener.get(), AsSockAddr(&addr), &addrlen), + SyscallSucceeds()); + switch (GetParam()) { + case AF_INET: + ASSERT_EQ(addrlen, sizeof(sockaddr_in)); + break; + case AF_INET6: + ASSERT_EQ(addrlen, sizeof(sockaddr_in6)); + break; + } + + // Before the listener socket receives a connection, it should not be eligible + // for reading. + struct pollfd poll_fd = {listener.get(), POLLIN, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, /* nfds */ 1, /* timeout */ 0), + SyscallSucceedsWithValue(0)); + + FileDescriptor connector = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + ASSERT_THAT(RetryEINTR(connect)(connector.get(), AsSockAddr(&addr), addrlen), + SyscallSucceeds()); + + // Now that a connection is pending, the listener is ready for a read. + ASSERT_THAT( + RetryEINTR(poll)(&poll_fd, /* nfds */ 1, /* infinite timeout */ -1), + SyscallSucceedsWithValue(1)); + + // Accept the connection. This should make the listener no longer ready for a + // read. + const FileDescriptor accepted = + ASSERT_NO_ERRNO_AND_VALUE(Accept(listener.get(), nullptr, nullptr)); + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, /* nfds*/ 1, /* timeout */ 0), + SyscallSucceedsWithValue(0)); +} + TEST_P(SimpleTcpSocketTest, NonBlockingConnectRetry) { const FileDescriptor listener = ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); From fc94225c333da8a1ed639da14c97bbc10c20cd0b Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Tue, 21 Mar 2023 13:18:19 -0700 Subject: [PATCH 27/49] Fix crash with large FD value There were 2 problems when trying to allocate a high FD value: - Rlimit is stored as uint64 and could be truncated when converting to int32 to calculate the max value allowed for the FD. - While trying to double the FD table size, the new length for the table could end up short due to invalid type convertion again. Reported-by: syzbot+e4a60cfb88b515cbd2b1@syzkaller.appspotmail.com PiperOrigin-RevId: 518362257 --- pkg/sentry/kernel/fd_table.go | 32 ++++++++++++++-------------- pkg/sentry/kernel/fd_table_unsafe.go | 17 +++++++++------ pkg/sentry/kernel/task.go | 4 ++-- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/pkg/sentry/kernel/fd_table.go b/pkg/sentry/kernel/fd_table.go index a8cdc38a3..8c90fe030 100644 --- a/pkg/sentry/kernel/fd_table.go +++ b/pkg/sentry/kernel/fd_table.go @@ -110,7 +110,7 @@ func (f *FDTable) loadDescriptorTable(m map[int32]descriptor) { panic(fmt.Sprintf("FD is not supposed to be negative. FD: %d", fd)) } - if file := f.set(ctx, fd, d.file, d.flags); file != nil { + if file := f.set(fd, d.file, d.flags); file != nil { panic("file set") } f.fdBitmap.Add(uint32(fd)) @@ -158,14 +158,14 @@ func (f *FDTable) DecRef(ctx context.Context) { // forEachUpTo iterates over all non-nil files upto maxFds (non-inclusive) in sorted order. // // It is the caller's responsibility to acquire an appropriate lock. -func (f *FDTable) forEachUpTo(ctx context.Context, maxFds int32, fn func(fd int32, file *vfs.FileDescription, flags FDFlags)) { +func (f *FDTable) forEachUpTo(ctx context.Context, maxFd int32, fn func(fd int32, file *vfs.FileDescription, flags FDFlags)) { // retries tracks the number of failed TryIncRef attempts for the same FD. retries := 0 fds := f.fdBitmap.ToSlice() // Iterate through the fdBitmap. for _, ufd := range fds { fd := int32(ufd) - if fd >= maxFds { + if fd >= maxFd { break } file, flags, ok := f.get(fd) @@ -246,7 +246,8 @@ func (f *FDTable) NewFDs(ctx context.Context, minFD int32, files []*vfs.FileDesc // Ensure we don't get past the provided limit. if limitSet := limits.FromContext(ctx); limitSet != nil { lim := limitSet.Get(limits.NumberOfFiles) - if lim.Cur != limits.Infinity { + // Only set if the limit is smaller than the max to avoid overflow. + if lim.Cur != limits.Infinity && lim.Cur < uint64(MaxFdLimit) { end = int32(lim.Cur) } if minFD+int32(len(files)) > end { @@ -258,7 +259,6 @@ func (f *FDTable) NewFDs(ctx context.Context, minFD int32, files []*vfs.FileDesc // max is used as the largest number in fdBitmap + 1. max := int32(0) - if !f.fdBitmap.IsEmpty() { max = int32(f.fdBitmap.Maximum()) max++ @@ -281,7 +281,7 @@ func (f *FDTable) NewFDs(ctx context.Context, minFD int32, files []*vfs.FileDesc break } f.fdBitmap.Add(fd) - f.set(ctx, int32(fd), files[len(fds)], flags) + f.set(int32(fd), files[len(fds)], flags) fds = append(fds, int32(fd)) minFD = int32(fd) } @@ -289,7 +289,7 @@ func (f *FDTable) NewFDs(ctx context.Context, minFD int32, files []*vfs.FileDesc // Failure? Unwind existing FDs. if len(fds) < len(files) { for _, i := range fds { - f.set(ctx, i, nil, FDFlags{}) + f.set(i, nil, FDFlags{}) f.fdBitmap.Remove(uint32(i)) } f.mu.Unlock() @@ -350,7 +350,7 @@ func (f *FDTable) newFDAt(ctx context.Context, fd int32, file *vfs.FileDescripti f.mu.Lock() defer f.mu.Unlock() - df := f.set(ctx, fd, file, flags) + df := f.set(fd, file, flags) // Add fd to fdBitmap. if file != nil { f.fdBitmap.Add(uint32(fd)) @@ -378,7 +378,7 @@ func (f *FDTable) SetFlags(ctx context.Context, fd int32, flags FDFlags) error { } // Update the flags. - f.set(ctx, fd, file, flags) + f.set(fd, file, flags) return nil } @@ -395,7 +395,7 @@ func (f *FDTable) SetFlagsForRange(ctx context.Context, startFd int32, endFd int for fd, err := f.fdBitmap.FirstOne(uint32(startFd)); err == nil && fd <= uint32(endFd); fd, err = f.fdBitmap.FirstOne(fd + 1) { fdI32 := int32(fd) file, _, _ := f.get(fdI32) - f.set(ctx, fdI32, file, flags) + f.set(fdI32, file, flags) } return nil @@ -452,14 +452,14 @@ func (f *FDTable) Exists(fd int32) bool { } // Fork returns an independent FDTable, cloning all FDs up to maxFds (non-inclusive). -func (f *FDTable) Fork(ctx context.Context, maxFds int32) *FDTable { +func (f *FDTable) Fork(ctx context.Context, maxFd int32) *FDTable { clone := f.k.NewFDTable() f.mu.Lock() defer f.mu.Unlock() - f.forEachUpTo(ctx, maxFds, func(fd int32, file *vfs.FileDescription, flags FDFlags) { + f.forEachUpTo(ctx, maxFd, func(fd int32, file *vfs.FileDescription, flags FDFlags) { // The set function here will acquire an appropriate table // reference for the clone. We don't need anything else. - if df := clone.set(ctx, fd, file, flags); df != nil { + if df := clone.set(fd, file, flags); df != nil { panic("file set") } clone.fdBitmap.Add(uint32(fd)) @@ -481,7 +481,7 @@ func (f *FDTable) Remove(ctx context.Context, fd int32) *vfs.FileDescription { if file != nil { // Add reference for caller. file.IncRef() - file = f.set(ctx, fd, nil, FDFlags{}) // Zap entry. + file = f.set(fd, nil, FDFlags{}) // Zap entry. f.fdBitmap.Remove(uint32(fd)) } f.mu.Unlock() @@ -499,7 +499,7 @@ func (f *FDTable) RemoveIf(ctx context.Context, cond func(*vfs.FileDescription, f.mu.Lock() f.forEach(ctx, func(fd int32, file *vfs.FileDescription, flags FDFlags) { if cond(file, flags) { - df := f.set(ctx, fd, nil, FDFlags{}) // Clear from table. + df := f.set(fd, nil, FDFlags{}) // Clear from table. f.fdBitmap.Remove(uint32(fd)) if df != nil { files = append(files, df) @@ -535,7 +535,7 @@ func (f *FDTable) RemoveNextInRange(ctx context.Context, startFd int32, endFd in if file != nil { // Add reference for caller. file.IncRef() - file = f.set(ctx, fd, nil, FDFlags{}) // Zap entry. + file = f.set(fd, nil, FDFlags{}) // Zap entry. f.fdBitmap.Remove(uint32(fd)) } f.mu.Unlock() diff --git a/pkg/sentry/kernel/fd_table_unsafe.go b/pkg/sentry/kernel/fd_table_unsafe.go index 549c1f073..2504c7564 100644 --- a/pkg/sentry/kernel/fd_table_unsafe.go +++ b/pkg/sentry/kernel/fd_table_unsafe.go @@ -20,7 +20,6 @@ import ( "unsafe" "gvisor.dev/gvisor/pkg/bitmap" - "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/sentry/vfs" ) @@ -79,16 +78,20 @@ func (f *FDTable) CurrentMaxFDs() int { // file after unlocking f.mu. // // Precondition: mu must be held. -func (f *FDTable) set(ctx context.Context, fd int32, file *vfs.FileDescription, flags FDFlags) *vfs.FileDescription { +func (f *FDTable) set(fd int32, file *vfs.FileDescription, flags FDFlags) *vfs.FileDescription { slicePtr := (*[]unsafe.Pointer)(atomic.LoadPointer(&f.slice)) // Grow the table as required. - if last := int32(len(*slicePtr)); fd >= last { - end := fd + 1 - if end < 2*last { - end = 2 * last + if length := len(*slicePtr); int(fd) >= length { + newLen := int(fd) + 1 + if newLen < 2*length { + // Ensure the table at least doubles in size without going over the limit. + newLen = 2 * length + if newLen > int(MaxFdLimit) { + newLen = int(MaxFdLimit) + } } - newSlice := append(*slicePtr, make([]unsafe.Pointer, end-last)...) + newSlice := append(*slicePtr, make([]unsafe.Pointer, newLen-length)...) slicePtr = &newSlice atomic.StorePointer(&f.slice, unsafe.Pointer(slicePtr)) } diff --git a/pkg/sentry/kernel/task.go b/pkg/sentry/kernel/task.go index 35b72e54c..b9e1eb52e 100644 --- a/pkg/sentry/kernel/task.go +++ b/pkg/sentry/kernel/task.go @@ -758,8 +758,8 @@ func (t *Task) NewFDs(fd int32, files []*vfs.FileDescription, flags FDFlags) ([] // This automatically passes the task as the context. // // Precondition: same as FDTable.Get. -func (t *Task) NewFDFrom(fd int32, file *vfs.FileDescription, flags FDFlags) (int32, error) { - return t.fdTable.NewFD(t, fd, file, flags) +func (t *Task) NewFDFrom(minFD int32, file *vfs.FileDescription, flags FDFlags) (int32, error) { + return t.fdTable.NewFD(t, minFD, file, flags) } // NewFDAt is a convenience wrapper for t.FDTable().NewFDAt. From 2ec9f07a9d3bbb37100c8fa31b1b55b5a602b213 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Tue, 21 Mar 2023 17:19:09 -0700 Subject: [PATCH 28/49] enable GRO in syscall tests GRO should be totally transparent and should not affect behavior. PiperOrigin-RevId: 518424809 --- runsc/boot/network.go | 20 +++++++++++++------- runsc/sandbox/network.go | 16 ++++++++++------ test/runner/main.go | 1 + 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/runsc/boot/network.go b/runsc/boot/network.go index b7b34c630..4f9c2d112 100644 --- a/runsc/boot/network.go +++ b/runsc/boot/network.go @@ -121,17 +121,19 @@ type XDPLink struct { LinkAddress net.HardwareAddr QDisc config.QueueingDiscipline Neighbors []Neighbor + GvisorGROTimeout time.Duration // NumChannels controls how many underlying FDs are to be used to // create this endpoint. NumChannels int } -// LoopbackLink configures a loopback li nk. +// LoopbackLink configures a loopback link. type LoopbackLink struct { - Name string - Addresses []IPWithPrefix - Routes []Route + Name string + Addresses []IPWithPrefix + Routes []Route + GvisorGROTimeout time.Duration } // CreateLinksAndRoutesArgs are arguments to CreateLinkAndRoutes. @@ -216,7 +218,10 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct linkEP := packetsocket.New(ethernet.New(loopback.New())) log.Infof("Enabling loopback interface %q with id %d on addresses %+v", link.Name, nicID, link.Addresses) - opts := stack.NICOptions{Name: link.Name} + opts := stack.NICOptions{ + Name: link.Name, + GROTimeout: link.GvisorGROTimeout, + } if err := n.createNICWithAddrs(nicID, linkEP, opts, link.Addresses); err != nil { return err } @@ -383,8 +388,9 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct log.Infof("Enabling interface %q with id %d on addresses %+v (%v) w/ %d channels", link.Name, nicID, link.Addresses, mac, link.NumChannels) opts := stack.NICOptions{ - Name: link.Name, - QDisc: qDisc, + Name: link.Name, + QDisc: qDisc, + GROTimeout: link.GvisorGROTimeout, } if err := n.createNICWithAddrs(nicID, sniffEP, opts, link.Addresses); err != nil { return err diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index f5b1a00b3..5a9773f19 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -60,7 +60,7 @@ func setupNetwork(conn *urpc.Client, pid int, conf *config.Config) error { switch conf.Network { case config.NetworkNone: log.Infof("Network is disabled, create loopback interface only") - if err := createDefaultLoopbackInterface(conn); err != nil { + if err := createDefaultLoopbackInterface(conf, conn); err != nil { return fmt.Errorf("creating default loopback interface: %v", err) } case config.NetworkSandbox: @@ -78,9 +78,11 @@ func setupNetwork(conn *urpc.Client, pid int, conf *config.Config) error { return nil } -func createDefaultLoopbackInterface(conn *urpc.Client) error { +func createDefaultLoopbackInterface(conf *config.Config, conn *urpc.Client) error { + link := boot.DefaultLoopbackLink + link.GvisorGROTimeout = conf.GvisorGROTimeout if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &boot.CreateLinksAndRoutesArgs{ - LoopbackLinks: []boot.LoopbackLink{boot.DefaultLoopbackLink}, + LoopbackLinks: []boot.LoopbackLink{link}, }, nil); err != nil { return fmt.Errorf("creating loopback link and routes: %v", err) } @@ -157,7 +159,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con // We build our own loopback device. if iface.Flags&net.FlagLoopback != 0 { - link, err := loopbackLink(iface, allAddrs) + link, err := loopbackLink(conf, iface, allAddrs) if err != nil { return fmt.Errorf("getting loopback link for iface %q: %w", iface.Name, err) } @@ -261,6 +263,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con Neighbors: neighbors, LinkAddress: linkAddress, Addresses: addresses, + GvisorGROTimeout: conf.GvisorGROTimeout, }) } else { link := boot.FDBasedLink{ @@ -492,9 +495,10 @@ func createSocketXDP(iface net.Interface) ([]*os.File, error) { // loopbackLink returns the link with addresses and routes for a loopback // interface. -func loopbackLink(iface net.Interface, addrs []net.Addr) (boot.LoopbackLink, error) { +func loopbackLink(conf *config.Config, iface net.Interface, addrs []net.Addr) (boot.LoopbackLink, error) { link := boot.LoopbackLink{ - Name: iface.Name, + Name: iface.Name, + GvisorGROTimeout: conf.GvisorGROTimeout, } for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) diff --git a/test/runner/main.go b/test/runner/main.go index c017499dd..5ad4ceaf7 100644 --- a/test/runner/main.go +++ b/test/runner/main.go @@ -229,6 +229,7 @@ func runRunsc(tc *gtest.TestCase, spec *specs.Spec) error { "-watchdog-action=panic", "-platform", *platform, "-file-access", *fileAccess, + "-gvisor-gro=200000ns", } if *network == "host" && !testutil.TestEnvSupportsRawSockets { From 0cbe6fc835840b3b99b458229eb785e9859faf0d Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Tue, 21 Mar 2023 22:08:06 -0700 Subject: [PATCH 29/49] systrap: introduce a spinning queue The spinning queue is a queue of spinning threads. It solves the fragmentation problem. The idea is to minimize the number of threads processing requests. We can't control how system threads are scheduled, so can't distribute requests efficiently. The spinning queue emulates virtual threads sorted by their spinning time. PiperOrigin-RevId: 518470754 --- pkg/sentry/platform/systrap/stub_unsafe.go | 8 + pkg/sentry/platform/systrap/subprocess.go | 17 ++ pkg/sentry/platform/systrap/sysmsg/sysmsg.go | 3 + pkg/sentry/platform/systrap/sysmsg/sysmsg.h | 4 +- .../platform/systrap/sysmsg/sysmsg_lib.c | 148 ++++++++++++++---- pkg/sentry/platform/systrap/systrap.go | 3 + 6 files changed, 155 insertions(+), 28 deletions(-) diff --git a/pkg/sentry/platform/systrap/stub_unsafe.go b/pkg/sentry/platform/systrap/stub_unsafe.go index 2f8aac0d7..7db687dc6 100644 --- a/pkg/sentry/platform/systrap/stub_unsafe.go +++ b/pkg/sentry/platform/systrap/stub_unsafe.go @@ -134,6 +134,7 @@ func stubInit() { // Add a guard page. mapLen += hostarch.PageSize stubSysmsgStack = mapLen + // Allocate maxGuestThreads plus ONE because each per-thread stack // has to be aligned to sysmsg.PerThreadMemSize. // Look at sysmsg/sighandler.c:sysmsg_addr() for more details. @@ -144,6 +145,9 @@ func stubInit() { stubContextQueueRegion = mapLen stubContextQueueRegionLen, _ = hostarch.PageRoundUp(unsafe.Sizeof(contextQueue{})) mapLen += stubContextQueueRegionLen + + stubSpinningThreadQueueAddr = mapLen + mapLen += sysmsg.SpinningQueueMemSize } // Allocate thread context region @@ -200,6 +204,7 @@ func stubInit() { stubSysmsgStack += stubStart stubROMapEnd += stubStart stubContextQueueRegion += stubStart + stubSpinningThreadQueueAddr += stubStart stubContextRegion += stubStart // Align stubSysmsgStack to the per-thread stack size. @@ -224,6 +229,9 @@ func stubInit() { *exp = 1 contextQueue := (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_queue_addr))) *contextQueue = uint64(stubContextQueueRegion) + + p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_spinning_queue_addr))) + *p = uint64(stubSpinningThreadQueueAddr) } prepareSeccompRules(stubSysmsgStart, stubSysmsgRules, stubSysmsgRulesLen) diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index b520b1eaa..70ebfe760 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -311,6 +311,7 @@ func newSubprocess(create func() (*thread, error), memoryFile *pgalloc.MemoryFil sp.unmap() sp.usertrap = usertrap.New() sp.mapSharedRegions() + sp.mapPrivateRegions() // Create the initial sysmsg thread. if contextDecouplingExp { @@ -391,6 +392,22 @@ func (s *subprocess) mapSharedRegions() { s.threadContextRegion = sentryThreadContextRegionAddr } +func (s *subprocess) mapPrivateRegions() { + if contextDecouplingExp { + _, err := s.syscallThread.syscall( + unix.SYS_MMAP, + arch.SyscallArgument{Value: uintptr(stubSpinningThreadQueueAddr)}, + arch.SyscallArgument{Value: uintptr(sysmsg.SpinningQueueMemSize)}, + arch.SyscallArgument{Value: uintptr(unix.PROT_READ | unix.PROT_WRITE)}, + arch.SyscallArgument{Value: uintptr(unix.MAP_PRIVATE | unix.MAP_ANONYMOUS | unix.MAP_FIXED)}, + arch.SyscallArgument{Value: 0}, + arch.SyscallArgument{Value: 0}) + if err != nil { + panic(fmt.Sprintf("failed to mmap spinning queue region into syscall thread: %v", err)) + } + } +} + // unmap unmaps non-stub regions of the process. // // This will panic on failure (which should never happen). diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go index 7448a1232..7c1e8fa8f 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go @@ -65,6 +65,9 @@ const ( // MsgOffsetFromStack is the offset of the Msg structure on // the thread stack. MsgOffsetFromSharedStack = PerThreadMemSize - hostarch.PageSize - PerThreadSharedStackOffset + + // SpinningQueueMemSize is the size of a spinning queue memory region. + SpinningQueueMemSize = hostarch.PageSize ) // StackAddrToMsg returns an address of a sysmsg structure. diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h index 445d2cbbc..16d973282 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h @@ -111,6 +111,7 @@ struct thread_context { #define GUARD_SIZE (PAGE_SIZE) #define MSG_OFFSET_FROM_START (PER_THREAD_MEM_SIZE - PAGE_SIZE) +#define SPINNING_QUEUE_MEM_SIZE PAGE_SIZE // LINT.ThenChange(sysmsg.go) #define FAULT_OPCODE 0x06 // "push %es" on x32 and invalid opcode on x64. @@ -123,7 +124,8 @@ extern uint64_t __export_pr_sched_core; extern uint64_t __export_deep_sleep_timeout; extern struct arch_state __export_arch_state; extern uint64_t __export_context_decoupling_exp; -extern uint64_t __export_context_queue_addr; +struct context_queue; +extern struct context_queue *__export_context_queue_addr; // NOLINTBEGIN(runtime/int) static void *sysmsg_sp() { diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c index 265e2a2d8..8be2c9b95 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,6 @@ // polling and fall asleep. uint64_t __export_deep_sleep_timeout; uint64_t __export_handshake_timeout; -uint64_t __export_context_queue_addr; // LINT.IfChange #define MAX_STUB_THREADS (4096) @@ -44,6 +44,9 @@ struct context_queue { uint32_t num_sleeping_threads; uint32_t ringbuffer[MAX_CONTEXT_QUEUE_ENTRIES]; }; + +struct context_queue *__export_context_queue_addr; + // LINT.ThenChange(../context_queue.go) uint32_t is_empty(struct context_queue *queue) { @@ -82,41 +85,132 @@ void memcpy(uint8_t *dest, uint8_t *src, size_t n) { } } +// The spinning queue is a queue of spinning threads. It solves the +// fragmentation problem. The idea is to minimize the number of threads +// processing requests. We can't control how system threads are scheduled, so +// can't distribute requests efficiently. The spinning queue emulates virtual +// threads sorted by their spinning time. +// +// This queue is lock-less to be sure that any thread scheduled out +// from CPU doesn't block others. +#define SPINNING_QUEUE_SIZE 128 + +// MAX_SPINNING_THREADS is half of SPINNING_QUEUE_SIZE to be sure that the tail +// doesn't catch the head. More details are in spinning_queue_remove_first. +#define MAX_SPINNING_THREADS (SPINNING_QUEUE_SIZE / 2) +struct spinning_queue { + uint32_t start; + uint32_t end; + uint64_t start_times[SPINNING_QUEUE_SIZE]; +}; + +struct spinning_queue *__export_spinning_queue_addr; + +// spinning_queue_push adds a new thread to the queue. It returns false if the +// queue if full. +static bool spinning_queue_push() __attribute__((warn_unused_result)); +static bool spinning_queue_push(void) { + struct spinning_queue *queue = __export_spinning_queue_addr; + uint32_t idx, start, end; + + BUILD_BUG_ON(sizeof(struct spinning_queue) > SPINNING_QUEUE_MEM_SIZE); + + end = __atomic_add_fetch(&queue->end, 1, __ATOMIC_SEQ_CST); + start = __atomic_load_n(&queue->start, __ATOMIC_SEQ_CST); + if (end - start > MAX_SPINNING_THREADS) { + __atomic_sub_fetch(&queue->end, 1, __ATOMIC_SEQ_CST); + return false; + } + + idx = end - 1; + __atomic_store_n(&queue->start_times[idx % SPINNING_QUEUE_SIZE], rdtsc(), + __ATOMIC_SEQ_CST); + return true; +} + +// spinning_queue_pop() removes one thread from a queue that has been spinning +// the shortest time. +static void spinning_queue_pop() { + struct spinning_queue *queue = __export_spinning_queue_addr; + + __atomic_add_fetch(&queue->end, -1, __ATOMIC_SEQ_CST); +} + +// spinning_queue_remove_first removes one thread from a queue that has been +// spinning longer than others and longer than a specified timeout. +// +// Returns true if one thread has been removed from the queue. +static bool spinning_queue_remove_first(uint64_t timeout) + __attribute__((warn_unused_result)); +static bool spinning_queue_remove_first(uint64_t timeout) { + struct spinning_queue *queue = __export_spinning_queue_addr; + uint64_t ts; + uint32_t idx; + + idx = __atomic_load_n(&queue->start, __ATOMIC_SEQ_CST); + ts = __atomic_load_n(&queue->start_times[idx % SPINNING_QUEUE_SIZE], + __ATOMIC_SEQ_CST); + if (ts == 0 || rdtsc() - ts < timeout) return false; + + // The current thread is still in a queue and the length of the queue is twice + // of the maximum number of threads, so we can zero the element and be sure + // that nobody is trying to set it in a non-zero value. + __atomic_store_n(&queue->start_times[idx % SPINNING_QUEUE_SIZE], 0, + __ATOMIC_SEQ_CST); + if (!__atomic_compare_exchange_n(&queue->start, &idx, idx + 1, false, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { + return false; + } + + return true; +} + +struct thread_context *queue_get_context(struct sysmsg *sysmsg) { + struct context_queue *queue = __export_context_queue_addr; + + while (!is_empty(queue)) { + uint32_t next = __atomic_load_n(&queue->start, __ATOMIC_ACQUIRE) % + MAX_CONTEXT_QUEUE_ENTRIES; + uint32_t context_id = __atomic_exchange_n( + &queue->ringbuffer[next], INVALID_CONTEXT_ID, __ATOMIC_ACQ_REL); + + if (context_id == INVALID_CONTEXT_ID) continue; + + __atomic_add_fetch(&queue->start, 1, __ATOMIC_ACQ_REL); + if (context_id > MAX_STUB_THREADS) { + panic(context_id); + } + sysmsg->context_id = context_id; + struct thread_context *ctx = thread_context_addr(sysmsg); + __atomic_store_n(&ctx->acked, 1, __ATOMIC_RELEASE); + __atomic_store_n(&ctx->thread_id, sysmsg->thread_id, __ATOMIC_RELEASE); + return ctx; + } + return NULL; +} + // get_context retrieves a context that is ready to be restored to the user. // This populates sysmsg->thread_context_id. struct thread_context *get_context(struct sysmsg *sysmsg) { - struct context_queue *queue = - (struct context_queue *)(__export_context_queue_addr); + struct context_queue *queue = __export_context_queue_addr; + for (;;) { + struct thread_context *ctx; + // Change sysmsg thread state just to indicate thread is not asleep. __atomic_store_n(&sysmsg->state, THREAD_STATE_PREP, __ATOMIC_RELEASE); - unsigned long start = rdtsc(); - for (;;) { - if (!is_empty(queue)) { - uint32_t next = __atomic_load_n(&queue->start, __ATOMIC_ACQUIRE) % - MAX_CONTEXT_QUEUE_ENTRIES; - uint32_t context_id = __atomic_exchange_n( - &queue->ringbuffer[next], INVALID_CONTEXT_ID, __ATOMIC_ACQ_REL); - if (context_id != INVALID_CONTEXT_ID) { - __atomic_add_fetch(&queue->start, 1, __ATOMIC_ACQ_REL); - if (context_id > MAX_STUB_THREADS) { - panic(context_id); - } - sysmsg->context_id = context_id; - struct thread_context *ctx = thread_context_addr(sysmsg); - __atomic_store_n(&ctx->acked, 1, __ATOMIC_RELEASE); - __atomic_store_n(&ctx->thread_id, sysmsg->thread_id, - __ATOMIC_RELEASE); + ctx = queue_get_context(sysmsg); + if (ctx) return ctx; + if (spinning_queue_push()) { + while (!spinning_queue_remove_first(__export_deep_sleep_timeout)) { + ctx = queue_get_context(sysmsg); + if (ctx) { + spinning_queue_pop(); return ctx; - } else { - continue; } - } - if ((rdtsc() - start) > __export_deep_sleep_timeout) { - break; - } - spinloop(); + spinloop(); + } } __atomic_store_n(&sysmsg->state, THREAD_STATE_ASLEEP, __ATOMIC_RELEASE); diff --git a/pkg/sentry/platform/systrap/systrap.go b/pkg/sentry/platform/systrap/systrap.go index eea34b3cd..28d8070a8 100644 --- a/pkg/sentry/platform/systrap/systrap.go +++ b/pkg/sentry/platform/systrap/systrap.go @@ -88,6 +88,9 @@ var ( stubSysmsgRules uintptr stubSysmsgRulesLen uintptr + stubSpinningThreadQueueAddr uintptr + stubSpinningThreadQueueSize uintptr + // stubROMapEnd is the end address of the read-only stub region that // contains the code and precompiled seccomp rules. stubROMapEnd uintptr From b72fc827f6199b02930449bb7b4cc502007530d5 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Tue, 21 Mar 2023 23:19:44 -0700 Subject: [PATCH 30/49] Update bazel to 6.1.1 --- images/default/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/default/Dockerfile b/images/default/Dockerfile index a7fcdeaed..921c45257 100644 --- a/images/default/Dockerfile +++ b/images/default/Dockerfile @@ -25,6 +25,6 @@ RUN curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud ln -s /google-cloud-sdk/bin/gcloud /usr/bin/gcloud # Download the official bazel binary. The APT repository isn't used because there is not packages for arm64. -RUN sh -c 'curl -o /usr/local/bin/bazel https://releases.bazel.build/6.0.0/release/bazel-6.0.0-linux-$(uname -m | sed s/aarch64/arm64/) && chmod ugo+x /usr/local/bin/bazel' +RUN sh -c 'curl -o /usr/local/bin/bazel https://releases.bazel.build/6.1.1/release/bazel-6.1.1-linux-$(uname -m | sed s/aarch64/arm64/) && chmod ugo+x /usr/local/bin/bazel' WORKDIR /workspace ENTRYPOINT ["/usr/local/bin/bazel"] From f8a73a7d1a2ebdbf49ce90b7dda0206e21d73c00 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 22 Mar 2023 09:55:04 -0700 Subject: [PATCH 31/49] Remove sysmsg->interrupted_context_id ctx->interrupt can be used to find out where the current context has to be interrupted or not. PiperOrigin-RevId: 518597531 --- pkg/sentry/platform/systrap/shared_context.go | 7 +++---- pkg/sentry/platform/systrap/subprocess.go | 7 +++++-- .../platform/systrap/sysmsg/sighandler_amd64.c | 18 +----------------- pkg/sentry/platform/systrap/sysmsg/sysmsg.go | 10 ++++------ pkg/sentry/platform/systrap/sysmsg/sysmsg.h | 3 +-- .../platform/systrap/sysmsg/sysmsg_lib.c | 1 + .../platform/systrap/sysmsg/sysmsg_offsets.h | 4 ++-- 7 files changed, 17 insertions(+), 33 deletions(-) diff --git a/pkg/sentry/platform/systrap/shared_context.go b/pkg/sentry/platform/systrap/shared_context.go index 41ffb996b..a3b295d0e 100644 --- a/pkg/sentry/platform/systrap/shared_context.go +++ b/pkg/sentry/platform/systrap/shared_context.go @@ -40,7 +40,7 @@ type sharedContext struct { subprocess *subprocess // contextID is the ID corresponding to the sysmsg.ThreadContext memory slot // that is used for this sharedContext. - contextID uint64 + contextID uint32 // shared is the handle to the shared memory that the sentry task goroutine // reads from and writes to. // NOTE: Using this handle directly without a getter from this function should @@ -59,7 +59,7 @@ func (s *subprocess) getSharedContext() (*sharedContext, error) { s.IncRef() sc := sharedContext{ subprocess: s, - contextID: id, + contextID: uint32(id), shared: s.getThreadContextFromID(id), } sc.shared.Init(invalidThreadID) @@ -71,7 +71,7 @@ func (sc *sharedContext) release() { if sc == nil { return } - sc.subprocess.threadContextPool.Put(sc.contextID) + sc.subprocess.threadContextPool.Put(uint64(sc.contextID)) sc.subprocess.DecRef(sc.subprocess.release) } @@ -102,7 +102,6 @@ func (sc *sharedContext) NotifyInterrupt() { } t := sysmsgThread.thread - atomic.StoreUint64(&sysmsgThread.msg.InterruptedContextID, sc.contextID) if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(t.tgid), uintptr(t.tid), uintptr(platform.SignalInterrupt)); e != 0 { panic(fmt.Sprintf("failed to interrupt the child process %d: %v", t.tid, e)) } diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index 70ebfe760..a2901f951 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -714,7 +714,10 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool c.signalInfo = linux.SignalInfo{Signo: int32(platform.SignalInterrupt)} return false, false, nil } - defer c.interrupt.Disable() + defer func() { + ctx.clearInterrupt() + c.interrupt.Disable() + }() if contextDecouplingExp { restoreFPState(nil, ctx, 0, c, ac) @@ -1036,7 +1039,7 @@ func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *a sysThread.setMsg(sysmsg.StackAddrToMsg(sentryStackAddr)) sysThread.msg.Init(threadID) if contextDecouplingExp { - sysThread.msg.ContextID = uint64(invalidContextID) + sysThread.msg.ContextID = invalidContextID } else { c.sharedContext.setThreadID(threadID) sysThread.msg.ContextID = c.sharedContext.contextID diff --git a/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c b/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c index 1161f89ad..193fe86f3 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c +++ b/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c @@ -211,23 +211,8 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { // If the current thread is in syshandler, an interrupt has to be postponed, // because sysmsg can't be changed. if (thread_state != THREAD_STATE_NONE) { - // There are two possibilities for when we received the interrupt: - // 1. Before syshandler switched to the sentry. - // In this case we do not need to postpone the interrupt because it - // will be handled as soon as the Task returns to the sentry kernel. - // 2. After syshandler has received a response from the sentry. - // This is an interrupt most likely targeted at whatever context is - // bound to the sysmsg right now, but there is an unlikely case that - // an interrupt takes a while to reach the stub and the context has - // changed. For this reason we write which context ID the interrupt - // was meant for in sysmsg and check against that. - uint64_t interrupted_tid = - __atomic_load_n(&sysmsg->interrupted_context_id, __ATOMIC_ACQUIRE); - if (thread_state == THREAD_STATE_DONE && - (interrupted_tid == sysmsg->context_id)) { + if (__atomic_load_n(&ctx->interrupt, __ATOMIC_ACQUIRE)) __atomic_store_n(&sysmsg->interrupt, 1, __ATOMIC_RELEASE); - __atomic_store_n(&ctx->interrupt, 1, __ATOMIC_RELAXED); - } return; } } else if (signo == SIGILL && sysmsg->state == THREAD_STATE_INTERRUPT) { @@ -376,7 +361,6 @@ void __syshandler() { ctx->siginfo.si_addr = 0; ctx->siginfo.si_syscall = ctx->ptregs.rax; ctx->ptregs.rax = (unsigned long)-ENOSYS; - __atomic_store_n(&sysmsg->interrupt, 0, __ATOMIC_RELAXED); switch_context_amd64(sysmsg, ctx, THREAD_STATE_EVENT, ctx_state); } diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go index 7c1e8fa8f..a103454b5 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go @@ -152,16 +152,14 @@ type Msg struct { // State indicates to the sentry what the sysmsg thread is doing at a given // moment. State ThreadState - // ContextID is the ID of the ThreadContext struct that the current - // sysmsg thread is is processing. This ID is used in the {sig|sys}handler - // to find the offset to the correct ThreadContext struct location. - ContextID uint64 // ContextRegion defines the ThreadContext memory region start within // the sysmsg thread address space. ContextRegion uint64 + // ContextID is the ID of the ThreadContext struct that the current + // sysmsg thread is is processing. This ID is used in the {sig|sys}handler + // to find the offset to the correct ThreadContext struct location. + ContextID uint32 - // InterruptedContextID is the target of the interrupt sent to sysmsg thread. - InterruptedContextID uint64 // FaultJump is the size of a faulted instruction. FaultJump int32 // Err is the error value with which the {sig|sys}handler crashes the stub diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h index 16d973282..b1def2359 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h @@ -57,12 +57,11 @@ struct sysmsg { uint64_t app_stack; uint32_t interrupt; uint32_t state; - uint64_t context_id; uint64_t context_region; + uint32_t context_id; // The fields above have offsets defined in sysmsg_offsets*.h - uint64_t interrupted_context_id; int32_t fault_jump; int32_t err; int32_t err_line; diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c index 8be2c9b95..ec5328d60 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c @@ -234,6 +234,7 @@ struct thread_context *switch_context(struct sysmsg *sysmsg, panic(ret); } } + uint32_t old_ctx_id = sysmsg->context_id; ctx = get_context(sysmsg); diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h index 7d6862393..fe02d4a68 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h @@ -39,8 +39,8 @@ #define offsetof_sysmsg_app_stack 0x20 #define offsetof_sysmsg_interrupt 0x28 #define offsetof_sysmsg_state 0x2c -#define offsetof_sysmsg_context_id 0x30 -#define offsetof_sysmsg_context_region 0x38 +#define offsetof_sysmsg_context_region 0x30 +#define offsetof_sysmsg_context_id 0x38 #define offsetof_thread_context_fpstate 0x0 #define offsetof_thread_context_fpstate_changed MAX_FPSTATE_LEN From 1063b2a08ecf618fa9fed6f1ff50b6fe16ba92b9 Mon Sep 17 00:00:00 2001 From: Nick Brown Date: Wed, 22 Mar 2023 10:46:09 -0700 Subject: [PATCH 32/49] Connect to bound addr before closing socket The `ConnectAndSendNoReceiver` test tries to setup a scenario in which a socket sends to an address with no listener, asserting on the returned ICMP error. In the existing code, this is done by closing a socket and then connecting/sending to its bound address with another socket. This is flaky because sometimes the system will coincidentally bind the sender to the bound address of the receiver, in which case we won't get the ICMP error. The fix is to connect before the socket is closed (that way the sender will get a different port than the boudn address). PiperOrigin-RevId: 518612786 --- test/syscalls/linux/udp_socket.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc index 345301049..8816af529 100644 --- a/test/syscalls/linux/udp_socket.cc +++ b/test/syscalls/linux/udp_socket.cc @@ -797,14 +797,15 @@ TEST_P(UdpSocketTest, SendToAddressOtherThanConnected) { TEST_P(UdpSocketTest, ConnectAndSendNoReceiver) { ASSERT_NO_ERRNO(BindLoopback()); - // Close the socket to release the port so that we get an ICMP error. - ASSERT_THAT(close(bind_.release()), SyscallSucceeds()); - // Connect to loopback:bind_addr_ which should *hopefully* not be bound by an // UDP socket. There is no easy way to ensure that the UDP port is not bound // by another conncurrently running test. *This is potentially flaky*. ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); + // Close the socket after connecting to the bound address to make sure `sock_` + // doesn't get auto-bound to the same port. + ASSERT_THAT(close(bind_.release()), SyscallSucceeds()); + char buf[512]; EXPECT_THAT(send(sock_.get(), buf, sizeof(buf), 0), SyscallSucceedsWithValue(sizeof(buf))); From ca7e83b67939809d5df619119fb3d5f958d67c86 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Wed, 22 Mar 2023 11:02:27 -0700 Subject: [PATCH 33/49] Internal change. PiperOrigin-RevId: 518617649 --- test/syscalls/linux/poll.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/syscalls/linux/poll.cc b/test/syscalls/linux/poll.cc index ccd084244..a80192599 100644 --- a/test/syscalls/linux/poll.cc +++ b/test/syscalls/linux/poll.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include #include @@ -350,6 +351,15 @@ TEST_F(PollTest, Nfds) { EXPECT_THAT(poll(fds.data(), max_fds + 1, 1), SyscallFailsWithErrno(EINVAL)); } +// Polling on a file that doesn't support blocking, like a directory, should +// immediately return. +TEST_F(PollTest, UnpollableFile) { + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/", O_RDONLY)); + struct pollfd poll_fd = {fd.get(), POLLIN | POLLOUT, 0}; + EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, -1), SyscallSucceedsWithValue(1)); + EXPECT_EQ(poll_fd.revents, POLLIN | POLLOUT); +} + } // namespace } // namespace testing } // namespace gvisor From 08920d098b3048167aafb7245a3476556367c528 Mon Sep 17 00:00:00 2001 From: Konstantin Bogomolov Date: Wed, 22 Mar 2023 11:31:56 -0700 Subject: [PATCH 34/49] Fix systrap TLS handling on ARM. With context decoupling off, TLS was not initialized properly because upon creation of a sysmsg thread the sighandler overwrote TLS with 0. The fix for this is to write the correct TLS only _after_ the sysmsg thread is initialized. With context decoupling on, retrieveArchSpecificState was not being used, which means that TLS was not saved to the sentry at all. So in this case, sysmsg threads would initially have the correct TLS value, but as soon as it changed during runtime it would become incorrect. Reported-by: syzbot+1cbe57d0e13ba2aa1898@syzkaller.appspotmail.com PiperOrigin-RevId: 518626789 --- pkg/sentry/platform/systrap/subprocess.go | 18 +++++++++--------- .../platform/systrap/subprocess_arm64.go | 4 +++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index a2901f951..ed2f71e86 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -694,19 +694,19 @@ func (t *thread) NotifyInterrupt() { // The second return value is true if a syscall instruction can be replaced on // a function call. func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool, shouldPatchSyscall bool, err error) { - // Reset necessary registers. - regs := &ac.StateData().Regs - s.resetSysemuRegs(regs) - ctx := c.sharedContext - ctx.shared.Regs = regs.PtraceRegs - restoreArchSpecificState(ctx.shared, ac) - // Get sysmsg thread bound to the context; no-op if contextDecoupling is on. + regs := &ac.StateData().Regs sysThread, err := s.getSysmsgThread(regs, c, ac) if err != nil { return false, false, err } + // Reset necessary registers. + s.resetSysemuRegs(regs) + ctx := c.sharedContext + ctx.shared.Regs = regs.PtraceRegs + restoreArchSpecificState(ctx.shared, ac) + // Check for interrupts, and ensure that future interrupts signal the context. if !c.interrupt.Enable(c.sharedContext) { // Pending interrupt; simulate. @@ -757,11 +757,11 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool return false, false, err } } - - retrieveArchSpecificState(ctx.shared, ac) } + // Copy register state locally. regs.PtraceRegs = ctx.shared.Regs + retrieveArchSpecificState(ctx.shared, ac) // We have a signal. We verify however, that the signal was // either delivered from the kernel or from this process. We // don't respect other signals. diff --git a/pkg/sentry/platform/systrap/subprocess_arm64.go b/pkg/sentry/platform/systrap/subprocess_arm64.go index 8d0d1ff9e..21db37029 100644 --- a/pkg/sentry/platform/systrap/subprocess_arm64.go +++ b/pkg/sentry/platform/systrap/subprocess_arm64.go @@ -193,7 +193,9 @@ func restoreArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) { func setArchSpecificRegs(sysThread *sysmsgThread, regs *arch.Registers) { if contextDecouplingExp { - // Set the start function and initial stack. + // Set the start function and initial stack. On ARM __export_start does not + // actually get used because we send a signal to the thread upon startup + // right away (see archSpecificSysmsgThreadInit below). regs.PtraceRegs.Pc = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_start)) regs.PtraceRegs.Sp = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr())) } From 44e2d0fcfeb641f3b8013c3f93cacdae447cc0f1 Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Wed, 22 Mar 2023 12:03:47 -0700 Subject: [PATCH 35/49] gVisor: Refactor `SyscallFn` to take in the syscall number as argument. This will be used to plumb the syscall number through to a counter metric that exports the number of times an unimplemented syscall has been called. Plenty of syscall implementations call `EmitUnimplementedEvent` for flags and settings that are not implemented. With `sysno` available, they will be able to plumb that bit of information through. PiperOrigin-RevId: 518635831 --- pkg/sentry/kernel/kernel.go | 2 +- pkg/sentry/kernel/syscalls.go | 2 +- pkg/sentry/kernel/table_test.go | 4 +- pkg/sentry/kernel/task_syscall.go | 2 +- pkg/sentry/syscalls/linux/linux64.go | 4 +- pkg/sentry/syscalls/linux/sys_afs_syscall.go | 2 +- pkg/sentry/syscalls/linux/sys_aio.go | 10 +- pkg/sentry/syscalls/linux/sys_capability.go | 4 +- pkg/sentry/syscalls/linux/sys_clone_amd64.go | 2 +- pkg/sentry/syscalls/linux/sys_clone_arm64.go | 2 +- pkg/sentry/syscalls/linux/sys_epoll.go | 14 +-- pkg/sentry/syscalls/linux/sys_eventfd.go | 6 +- pkg/sentry/syscalls/linux/sys_file.go | 104 +++++++++---------- pkg/sentry/syscalls/linux/sys_futex.go | 8 +- pkg/sentry/syscalls/linux/sys_getdents.go | 4 +- pkg/sentry/syscalls/linux/sys_identity.go | 28 ++--- pkg/sentry/syscalls/linux/sys_inotify.go | 10 +- pkg/sentry/syscalls/linux/sys_iouring.go | 4 +- pkg/sentry/syscalls/linux/sys_membarrier.go | 4 +- pkg/sentry/syscalls/linux/sys_mempolicy.go | 6 +- pkg/sentry/syscalls/linux/sys_mmap.go | 26 ++--- pkg/sentry/syscalls/linux/sys_mount.go | 4 +- pkg/sentry/syscalls/linux/sys_mq.go | 4 +- pkg/sentry/syscalls/linux/sys_msgqueue.go | 8 +- pkg/sentry/syscalls/linux/sys_pipe.go | 4 +- pkg/sentry/syscalls/linux/sys_poll.go | 8 +- pkg/sentry/syscalls/linux/sys_prctl.go | 8 +- pkg/sentry/syscalls/linux/sys_process_vm.go | 4 +- pkg/sentry/syscalls/linux/sys_random.go | 2 +- pkg/sentry/syscalls/linux/sys_read_write.go | 24 ++--- pkg/sentry/syscalls/linux/sys_rlimit.go | 6 +- pkg/sentry/syscalls/linux/sys_rseq.go | 4 +- pkg/sentry/syscalls/linux/sys_rusage.go | 4 +- pkg/sentry/syscalls/linux/sys_sched.go | 10 +- pkg/sentry/syscalls/linux/sys_seccomp.go | 2 +- pkg/sentry/syscalls/linux/sys_sem.go | 10 +- pkg/sentry/syscalls/linux/sys_shm.go | 10 +- pkg/sentry/syscalls/linux/sys_signal.go | 34 +++--- pkg/sentry/syscalls/linux/sys_socket.go | 36 +++---- pkg/sentry/syscalls/linux/sys_splice.go | 6 +- pkg/sentry/syscalls/linux/sys_stat.go | 14 +-- pkg/sentry/syscalls/linux/sys_sync.go | 14 +-- pkg/sentry/syscalls/linux/sys_sysinfo.go | 2 +- pkg/sentry/syscalls/linux/sys_syslog.go | 2 +- pkg/sentry/syscalls/linux/sys_thread.go | 54 +++++----- pkg/sentry/syscalls/linux/sys_time.go | 14 +-- pkg/sentry/syscalls/linux/sys_timer.go | 16 +-- pkg/sentry/syscalls/linux/sys_timerfd.go | 6 +- pkg/sentry/syscalls/linux/sys_tls_amd64.go | 4 +- pkg/sentry/syscalls/linux/sys_tls_arm64.go | 2 +- pkg/sentry/syscalls/linux/sys_utsname.go | 6 +- pkg/sentry/syscalls/linux/sys_xattr.go | 24 ++--- pkg/sentry/syscalls/syscalls.go | 10 +- 53 files changed, 302 insertions(+), 302 deletions(-) diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index 260fcc919..be17dc39a 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -1534,7 +1534,7 @@ const ( // EmitUnimplementedEvent emits an UnimplementedSyscall event via the event // channel. -func (k *Kernel) EmitUnimplementedEvent(ctx context.Context) { +func (k *Kernel) EmitUnimplementedEvent(ctx context.Context, sysno uintptr) { k.unimplementedSyscallEmitterOnce.Do(func() { k.unimplementedSyscallEmitter = eventchannel.RateLimitedEmitterFrom(eventchannel.DefaultEmitter, unimplementedSyscallsMaxRate, unimplementedSyscallBurst) }) diff --git a/pkg/sentry/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index fe9877a1f..55fe2cf4c 100644 --- a/pkg/sentry/kernel/syscalls.go +++ b/pkg/sentry/kernel/syscalls.go @@ -90,7 +90,7 @@ type Syscall struct { } // SyscallFn is a syscall implementation. -type SyscallFn func(t *Task, args arch.SyscallArguments) (uintptr, *SyscallControl, error) +type SyscallFn func(t *Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *SyscallControl, error) // MissingFn is a syscall to be called when an implementation is missing. type MissingFn func(t *Task, sysno uintptr, args arch.SyscallArguments) (uintptr, error) diff --git a/pkg/sentry/kernel/table_test.go b/pkg/sentry/kernel/table_test.go index 32cf47e05..f76df30b7 100644 --- a/pkg/sentry/kernel/table_test.go +++ b/pkg/sentry/kernel/table_test.go @@ -30,7 +30,7 @@ func createSyscallTable() *SyscallTable { for i := uintptr(0); i <= maxTestSyscall; i++ { j := i m[i] = Syscall{ - Fn: func(*Task, arch.SyscallArguments) (uintptr, *SyscallControl, error) { + Fn: func(t *Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *SyscallControl, error) { return j, nil, nil }, } @@ -61,7 +61,7 @@ func TestTable(t *testing.T) { continue } - v, _, _ := fn(nil, arch.SyscallArguments{}) + v, _, _ := fn(nil, i, arch.SyscallArguments{}) if v != i { t.Errorf("Wrong return value for syscall %v: expected %v, got %v", i, i, v) } diff --git a/pkg/sentry/kernel/task_syscall.go b/pkg/sentry/kernel/task_syscall.go index 31e934507..11592e300 100644 --- a/pkg/sentry/kernel/task_syscall.go +++ b/pkg/sentry/kernel/task_syscall.go @@ -139,7 +139,7 @@ func (t *Task) executeSyscall(sysno uintptr, args arch.SyscallArguments) (rval u } if fn != nil { // Call our syscall implementation. - rval, ctrl, err = fn(t, args) + rval, ctrl, err = fn(t, sysno, args) } else { // Use the missing function if not found. rval, err = t.SyscallTable().Missing(t, sysno, args) diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index 34780cfd0..d6159f28c 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -414,7 +414,7 @@ var AMD64 = &kernel.SyscallTable{ 0xffffffffff600800: 309, // vsyscall getcpu(2) }, Missing: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, linuxerr.ENOSYS }, } @@ -731,7 +731,7 @@ var ARM64 = &kernel.SyscallTable{ }, Emulate: map[hostarch.Addr]uintptr{}, Missing: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, linuxerr.ENOSYS }, } diff --git a/pkg/sentry/syscalls/linux/sys_afs_syscall.go b/pkg/sentry/syscalls/linux/sys_afs_syscall.go index 9bc0ef560..69711e05b 100644 --- a/pkg/sentry/syscalls/linux/sys_afs_syscall.go +++ b/pkg/sentry/syscalls/linux/sys_afs_syscall.go @@ -35,7 +35,7 @@ func SetAFSSyscallPanic(v bool) { // AFSSyscall is a gVisor specific implementation of afs_syscall: // - if TESTONLY-afs-syscall-panic flag is set it triggers a panic. -func AFSSyscall(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func AFSSyscall(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { if afsSyscallPanic.Load() { panic("User workload triggered a panic via afs_syscall. This panic is intentional.") } diff --git a/pkg/sentry/syscalls/linux/sys_aio.go b/pkg/sentry/syscalls/linux/sys_aio.go index 010b83f8c..7e696f1d8 100644 --- a/pkg/sentry/syscalls/linux/sys_aio.go +++ b/pkg/sentry/syscalls/linux/sys_aio.go @@ -30,7 +30,7 @@ import ( ) // IoSetup implements linux syscall io_setup(2). -func IoSetup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func IoSetup(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { nrEvents := args[0].Int() idAddr := args[1].Pointer() @@ -60,7 +60,7 @@ func IoSetup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // IoDestroy implements linux syscall io_destroy(2). -func IoDestroy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func IoDestroy(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := args[0].Uint64() ctx := t.MemoryManager().DestroyAIOContext(t, id) @@ -88,7 +88,7 @@ func IoDestroy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // IoGetevents implements linux syscall io_getevents(2). -func IoGetevents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func IoGetevents(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := args[0].Uint64() minEvents := args[1].Int() events := args[2].Int() @@ -214,12 +214,12 @@ func memoryFor(t *kernel.Task, cb *linux.IOCallback) (usermem.IOSequence, error) // // It is not presently supported (ENOSYS indicates no support on this // architecture). -func IoCancel(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func IoCancel(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, linuxerr.ENOSYS } // IoSubmit implements linux syscall io_submit(2). -func IoSubmit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func IoSubmit(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := args[0].Uint64() nrEvents := args[1].Int() addr := args[2].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_capability.go b/pkg/sentry/syscalls/linux/sys_capability.go index 1e714503c..44aeab7e3 100644 --- a/pkg/sentry/syscalls/linux/sys_capability.go +++ b/pkg/sentry/syscalls/linux/sys_capability.go @@ -40,7 +40,7 @@ func lookupCaps(t *kernel.Task, tid kernel.ThreadID) (permitted, inheritable, ef } // Capget implements Linux syscall capget. -func Capget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Capget(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { hdrAddr := args[0].Pointer() dataAddr := args[1].Pointer() @@ -104,7 +104,7 @@ func Capget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Capset implements Linux syscall capset. -func Capset(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Capset(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { hdrAddr := args[0].Pointer() dataAddr := args[1].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_clone_amd64.go b/pkg/sentry/syscalls/linux/sys_clone_amd64.go index e068d366a..0171a3be9 100644 --- a/pkg/sentry/syscalls/linux/sys_clone_amd64.go +++ b/pkg/sentry/syscalls/linux/sys_clone_amd64.go @@ -27,7 +27,7 @@ import ( // x86_64: // // sys_clone(clone_flags, newsp, parent_tidptr, child_tidptr, tls_val) -func Clone(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Clone(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { flags := int(args[0].Int()) stack := args[1].Pointer() parentTID := args[2].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_clone_arm64.go b/pkg/sentry/syscalls/linux/sys_clone_arm64.go index fa2bb4299..0a76da954 100644 --- a/pkg/sentry/syscalls/linux/sys_clone_arm64.go +++ b/pkg/sentry/syscalls/linux/sys_clone_arm64.go @@ -27,7 +27,7 @@ import ( // arm64(kernel/fork.c with CONFIG_CLONE_BACKWARDS defined in the config file): // // sys_clone(clone_flags, newsp, parent_tidptr, tls_val, child_tidptr) -func Clone(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Clone(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { flags := int(args[0].Int()) stack := args[1].Pointer() parentTID := args[2].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_epoll.go b/pkg/sentry/syscalls/linux/sys_epoll.go index 4d4a4db69..98ba6ce8c 100644 --- a/pkg/sentry/syscalls/linux/sys_epoll.go +++ b/pkg/sentry/syscalls/linux/sys_epoll.go @@ -31,7 +31,7 @@ import ( var sizeofEpollEvent = (*linux.EpollEvent)(nil).SizeBytes() // EpollCreate1 implements Linux syscall epoll_create1(2). -func EpollCreate1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func EpollCreate1(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { flags := args[0].Int() if flags&^linux.EPOLL_CLOEXEC != 0 { return 0, nil, linuxerr.EINVAL @@ -53,7 +53,7 @@ func EpollCreate1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // EpollCreate implements Linux syscall epoll_create(2). -func EpollCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func EpollCreate(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { size := args[0].Int() // "Since Linux 2.6.8, the size argument is ignored, but must be greater @@ -76,7 +76,7 @@ func EpollCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S } // EpollCtl implements Linux syscall epoll_ctl(2). -func EpollCtl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func EpollCtl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { epfd := args[0].Int() op := args[1].Int() fd := args[2].Int() @@ -187,7 +187,7 @@ func waitEpoll(t *kernel.Task, epfd int32, eventsAddr hostarch.Addr, maxEvents i } // EpollWait implements Linux syscall epoll_wait(2). -func EpollWait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func EpollWait(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { epfd := args[0].Int() eventsAddr := args[1].Pointer() maxEvents := int(args[2].Int()) @@ -197,7 +197,7 @@ func EpollWait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // EpollPwait implements Linux syscall epoll_pwait(2). -func EpollPwait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func EpollPwait(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { maskAddr := args[4].Pointer() maskSize := uint(args[5].Uint()) @@ -205,11 +205,11 @@ func EpollPwait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy return 0, nil, err } - return EpollWait(t, args) + return EpollWait(t, sysno, args) } // EpollPwait2 implements Linux syscall epoll_pwait(2). -func EpollPwait2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func EpollPwait2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { epfd := args[0].Int() eventsAddr := args[1].Pointer() maxEvents := int(args[2].Int()) diff --git a/pkg/sentry/syscalls/linux/sys_eventfd.go b/pkg/sentry/syscalls/linux/sys_eventfd.go index 051473e0d..950493c74 100644 --- a/pkg/sentry/syscalls/linux/sys_eventfd.go +++ b/pkg/sentry/syscalls/linux/sys_eventfd.go @@ -23,7 +23,7 @@ import ( ) // Eventfd2 implements linux syscall eventfd2(2). -func Eventfd2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Eventfd2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { initVal := uint64(args[0].Uint()) flags := uint(args[1].Uint()) allOps := uint(linux.EFD_SEMAPHORE | linux.EFD_NONBLOCK | linux.EFD_CLOEXEC) @@ -55,7 +55,7 @@ func Eventfd2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // Eventfd implements linux syscall eventfd(2). -func Eventfd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Eventfd(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { args[1].Value = 0 - return Eventfd2(t, args) + return Eventfd2(t, sysno, args) } diff --git a/pkg/sentry/syscalls/linux/sys_file.go b/pkg/sentry/syscalls/linux/sys_file.go index c69add46c..292ab9e72 100644 --- a/pkg/sentry/syscalls/linux/sys_file.go +++ b/pkg/sentry/syscalls/linux/sys_file.go @@ -35,7 +35,7 @@ import ( ) // Mknod implements Linux syscall mknod(2). -func Mknod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mknod(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() mode := args[1].ModeT() dev := args[2].Uint() @@ -43,7 +43,7 @@ func Mknod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Mknodat implements Linux syscall mknodat(2). -func Mknodat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mknodat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() addr := args[1].Pointer() mode := args[2].ModeT() @@ -75,7 +75,7 @@ func mknodat(t *kernel.Task, dirfd int32, addr hostarch.Addr, mode linux.FileMod } // Open implements Linux syscall open(2). -func Open(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Open(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() flags := args[1].Uint() mode := args[2].ModeT() @@ -83,7 +83,7 @@ func Open(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC } // Openat implements Linux syscall openat(2). -func Openat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Openat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() addr := args[1].Pointer() flags := args[2].Uint() @@ -92,7 +92,7 @@ func Openat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Creat implements Linux syscall creat(2). -func Creat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Creat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() mode := args[1].ModeT() return openat(t, linux.AT_FDCWD, addr, linux.O_WRONLY|linux.O_CREAT|linux.O_TRUNC, mode) @@ -125,7 +125,7 @@ func openat(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr, flags uint32, m } // Access implements Linux syscall access(2). -func Access(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Access(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() mode := args[1].ModeT() @@ -133,7 +133,7 @@ func Access(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Faccessat implements Linux syscall faccessat(2). -func Faccessat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Faccessat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() addr := args[1].Pointer() mode := args[2].ModeT() @@ -142,7 +142,7 @@ func Faccessat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Faccessat2 implements Linux syscall faccessat2(2). -func Faccessat2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Faccessat2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() addr := args[1].Pointer() mode := args[2].ModeT() @@ -199,7 +199,7 @@ func accessAt(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr, mode uint, fl } // Ioctl implements Linux syscall ioctl(2). -func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Ioctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() file := t.GetFile(fd) @@ -288,7 +288,7 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Getcwd implements Linux syscall getcwd(2). -func Getcwd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getcwd(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() size := args[1].SizeT() @@ -320,7 +320,7 @@ func Getcwd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Chdir implements Linux syscall chdir(2). -func Chdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Chdir(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() path, err := copyInPath(t, addr) @@ -345,7 +345,7 @@ func Chdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Fchdir implements Linux syscall fchdir(2). -func Fchdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fchdir(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() tpop, err := getTaskPathOperation(t, fd, fspath.Path{}, allowEmptyPath, nofollowFinalSymlink) @@ -366,7 +366,7 @@ func Fchdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Chroot implements Linux syscall chroot(2). -func Chroot(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Chroot(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() if !t.HasCapability(linux.CAP_SYS_CHROOT) { @@ -395,7 +395,7 @@ func Chroot(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // PivotRoot implements Linux syscall pivot_root(2). -func PivotRoot(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func PivotRoot(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr1 := args[0].Pointer() addr2 := args[1].Pointer() @@ -440,7 +440,7 @@ func PivotRoot(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Close implements Linux syscall close(2). -func Close(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Close(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() // Note that Remove provides a reference on the file that we may use to @@ -457,7 +457,7 @@ func Close(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // CloseRange implements linux syscall close_range(2). -func CloseRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func CloseRange(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { first := args[0].Uint() last := args[1].Uint() flags := args[2].Uint() @@ -511,7 +511,7 @@ func CloseRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy } // Dup implements Linux syscall dup(2). -func Dup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Dup(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() file := t.GetFile(fd) @@ -528,7 +528,7 @@ func Dup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo } // Dup2 implements Linux syscall dup2(2). -func Dup2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Dup2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { oldfd := args[0].Int() newfd := args[1].Int() @@ -546,7 +546,7 @@ func Dup2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC } // Dup3 implements Linux syscall dup3(2). -func Dup3(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Dup3(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { oldfd := args[0].Int() newfd := args[1].Int() flags := args[2].Uint() @@ -579,7 +579,7 @@ func dup3(t *kernel.Task, oldfd, newfd int32, flags uint32) (uintptr, *kernel.Sy } // Fcntl implements linux syscall fcntl(2). -func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fcntl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() cmd := args[1].Int() @@ -879,7 +879,7 @@ func posixLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDescrip // Fadvise64 implements fadvise64(2). // This implementation currently ignores the provided advice. -func Fadvise64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fadvise64(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() length := args[2].Int64() advice := args[3].Int() @@ -920,14 +920,14 @@ func Fadvise64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Mkdir implements Linux syscall mkdir(2). -func Mkdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mkdir(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() mode := args[1].ModeT() return 0, nil, mkdirat(t, linux.AT_FDCWD, addr, mode) } // Mkdirat implements Linux syscall mkdirat(2). -func Mkdirat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mkdirat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() addr := args[1].Pointer() mode := args[2].ModeT() @@ -950,7 +950,7 @@ func mkdirat(t *kernel.Task, dirfd int32, addr hostarch.Addr, mode uint) error { } // Rmdir implements Linux syscall rmdir(2). -func Rmdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Rmdir(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() return 0, nil, rmdirat(t, linux.AT_FDCWD, pathAddr) } @@ -969,14 +969,14 @@ func rmdirat(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr) error { } // Symlink implements Linux syscall symlink(2). -func Symlink(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Symlink(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { targetAddr := args[0].Pointer() linkpathAddr := args[1].Pointer() return 0, nil, symlinkat(t, targetAddr, linux.AT_FDCWD, linkpathAddr) } // Symlinkat implements Linux syscall symlinkat(2). -func Symlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Symlinkat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { targetAddr := args[0].Pointer() newdirfd := args[1].Int() linkpathAddr := args[2].Pointer() @@ -1004,14 +1004,14 @@ func symlinkat(t *kernel.Task, targetAddr hostarch.Addr, newdirfd int32, linkpat } // Link implements Linux syscall link(2). -func Link(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Link(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { oldpathAddr := args[0].Pointer() newpathAddr := args[1].Pointer() return 0, nil, linkat(t, linux.AT_FDCWD, oldpathAddr, linux.AT_FDCWD, newpathAddr, 0 /* flags */) } // Linkat implements Linux syscall linkat(2). -func Linkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Linkat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { olddirfd := args[0].Int() oldpathAddr := args[1].Pointer() newdirfd := args[2].Int() @@ -1052,7 +1052,7 @@ func linkat(t *kernel.Task, olddirfd int32, oldpathAddr hostarch.Addr, newdirfd } // Readlinkat implements Linux syscall readlinkat(2). -func Readlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Readlinkat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() bufAddr := args[2].Pointer() @@ -1061,7 +1061,7 @@ func Readlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy } // Readlink implements Linux syscall readlink(2). -func Readlink(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Readlink(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() bufAddr := args[1].Pointer() size := args[2].SizeT() @@ -1102,7 +1102,7 @@ func readlinkat(t *kernel.Task, dirfd int32, pathAddr, bufAddr hostarch.Addr, si } // Unlink implements Linux syscall unlink(2). -func Unlink(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Unlink(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() return 0, nil, unlinkat(t, linux.AT_FDCWD, pathAddr) } @@ -1121,7 +1121,7 @@ func unlinkat(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr) error { } // Unlinkat implements Linux syscall unlinkat(2). -func Unlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Unlinkat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() flags := args[2].Int() @@ -1184,7 +1184,7 @@ func handleSetSizeError(t *kernel.Task, err error) error { } // Truncate implements Linux syscall truncate(2). -func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Truncate(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].Int64() @@ -1208,7 +1208,7 @@ func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // Ftruncate implements Linux syscall ftruncate(2). -func Ftruncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Ftruncate(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() length := args[1].Int64() @@ -1236,14 +1236,14 @@ func Ftruncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Umask implements linux syscall umask(2). -func Umask(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Umask(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { mask := args[0].ModeT() mask = t.FSContext().SwapUmask(mask & 0777) return uintptr(mask), nil, nil } // Chown implements Linux syscall chown(2). -func Chown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Chown(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() owner := args[1].Int() group := args[2].Int() @@ -1251,7 +1251,7 @@ func Chown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Lchown implements Linux syscall lchown(2). -func Lchown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Lchown(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() owner := args[1].Int() group := args[2].Int() @@ -1259,7 +1259,7 @@ func Lchown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Fchownat implements Linux syscall fchownat(2). -func Fchownat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fchownat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() owner := args[2].Int() @@ -1308,7 +1308,7 @@ func populateSetStatOptionsForChown(t *kernel.Task, owner, group int32, opts *vf } // Fchown implements Linux syscall fchown(2). -func Fchown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fchown(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() owner := args[1].Int() group := args[2].Int() @@ -1329,14 +1329,14 @@ func Fchown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal const chmodMask = 0777 | linux.S_ISUID | linux.S_ISGID | linux.S_ISVTX // Chmod implements Linux syscall chmod(2). -func Chmod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Chmod(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() mode := args[1].ModeT() return 0, nil, fchmodat(t, linux.AT_FDCWD, pathAddr, mode) } // Fchmodat implements Linux syscall fchmodat(2). -func Fchmodat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fchmodat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() mode := args[2].ModeT() @@ -1358,7 +1358,7 @@ func fchmodat(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr, mode uint) er } // Fchmod implements Linux syscall fchmod(2). -func Fchmod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fchmod(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() mode := args[1].ModeT() @@ -1377,7 +1377,7 @@ func Fchmod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Utime implements Linux syscall utime(2). -func Utime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Utime(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() timesAddr := args[1].Pointer() @@ -1407,7 +1407,7 @@ func Utime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Utimes implements Linux syscall utimes(2). -func Utimes(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Utimes(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() timesAddr := args[1].Pointer() @@ -1425,7 +1425,7 @@ func Utimes(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Futimesat implements Linux syscall futimesat(2). -func Futimesat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Futimesat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() timesAddr := args[2].Pointer() @@ -1479,7 +1479,7 @@ func populateSetStatOptionsForUtimes(t *kernel.Task, timesAddr hostarch.Addr, op } // Utimensat implements Linux syscall utimensat(2). -func Utimensat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Utimensat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() timesAddr := args[2].Pointer() @@ -1551,14 +1551,14 @@ func populateSetStatOptionsForUtimens(t *kernel.Task, timesAddr hostarch.Addr, o } // Rename implements Linux syscall rename(2). -func Rename(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Rename(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { oldpathAddr := args[0].Pointer() newpathAddr := args[1].Pointer() return 0, nil, renameat(t, linux.AT_FDCWD, oldpathAddr, linux.AT_FDCWD, newpathAddr, 0 /* flags */) } // Renameat implements Linux syscall renameat(2). -func Renameat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Renameat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { olddirfd := args[0].Int() oldpathAddr := args[1].Pointer() newdirfd := args[2].Int() @@ -1567,7 +1567,7 @@ func Renameat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // Renameat2 implements Linux syscall renameat2(2). -func Renameat2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Renameat2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { olddirfd := args[0].Int() oldpathAddr := args[1].Pointer() newdirfd := args[2].Int() @@ -1604,7 +1604,7 @@ func renameat(t *kernel.Task, olddirfd int32, oldpathAddr hostarch.Addr, newdirf } // Fallocate implements linux system call fallocate(2). -func Fallocate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fallocate(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() mode := args[1].Uint64() offset := args[2].Int64() @@ -1643,7 +1643,7 @@ func Fallocate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Flock implements linux syscall flock(2). -func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Flock(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() operation := args[1].Int() @@ -1685,7 +1685,7 @@ const ( ) // MemfdCreate implements the linux syscall memfd_create(2). -func MemfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func MemfdCreate(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() flags := args[1].Uint() diff --git a/pkg/sentry/syscalls/linux/sys_futex.go b/pkg/sentry/syscalls/linux/sys_futex.go index dcf12dde3..d89178bbd 100644 --- a/pkg/sentry/syscalls/linux/sys_futex.go +++ b/pkg/sentry/syscalls/linux/sys_futex.go @@ -167,7 +167,7 @@ func tryLockPI(t *kernel.Task, addr hostarch.Addr, private bool) error { // Futex implements linux syscall futex(2). // It provides a method for a program to wait for a value at a given address to // change, and a method to wake up anyone waiting on a particular address. -func Futex(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Futex(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() futexOp := args[1].Int() val := int(args[2].Int()) @@ -278,7 +278,7 @@ func Futex(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall return 0, nil, err case linux.FUTEX_WAIT_REQUEUE_PI, linux.FUTEX_CMP_REQUEUE_PI: - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, nil, linuxerr.ENOSYS default: @@ -288,7 +288,7 @@ func Futex(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // SetRobustList implements linux syscall set_robust_list(2). -func SetRobustList(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SetRobustList(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // Despite the syscall using the name 'pid' for this variable, it is // very much a tid. head := args[0].Pointer() @@ -302,7 +302,7 @@ func SetRobustList(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel } // GetRobustList implements linux syscall get_robust_list(2). -func GetRobustList(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func GetRobustList(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // Despite the syscall using the name 'pid' for this variable, it is // very much a tid. tid := args[0].Int() diff --git a/pkg/sentry/syscalls/linux/sys_getdents.go b/pkg/sentry/syscalls/linux/sys_getdents.go index 528254eff..7e2e08f68 100644 --- a/pkg/sentry/syscalls/linux/sys_getdents.go +++ b/pkg/sentry/syscalls/linux/sys_getdents.go @@ -27,12 +27,12 @@ import ( ) // Getdents implements Linux syscall getdents(2). -func Getdents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getdents(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return getdents(t, args, false /* isGetdents64 */) } // Getdents64 implements Linux syscall getdents64(2). -func Getdents64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getdents64(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return getdents(t, args, true /* isGetdents64 */) } diff --git a/pkg/sentry/syscalls/linux/sys_identity.go b/pkg/sentry/syscalls/linux/sys_identity.go index 50fcadb58..fc49f573a 100644 --- a/pkg/sentry/syscalls/linux/sys_identity.go +++ b/pkg/sentry/syscalls/linux/sys_identity.go @@ -27,21 +27,21 @@ const ( ) // Getuid implements the Linux syscall getuid. -func Getuid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getuid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { c := t.Credentials() ruid := c.RealKUID.In(c.UserNamespace).OrOverflow() return uintptr(ruid), nil, nil } // Geteuid implements the Linux syscall geteuid. -func Geteuid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Geteuid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { c := t.Credentials() euid := c.EffectiveKUID.In(c.UserNamespace).OrOverflow() return uintptr(euid), nil, nil } // Getresuid implements the Linux syscall getresuid. -func Getresuid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getresuid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { ruidAddr := args[0].Pointer() euidAddr := args[1].Pointer() suidAddr := args[2].Pointer() @@ -62,21 +62,21 @@ func Getresuid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Getgid implements the Linux syscall getgid. -func Getgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getgid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { c := t.Credentials() rgid := c.RealKGID.In(c.UserNamespace).OrOverflow() return uintptr(rgid), nil, nil } // Getegid implements the Linux syscall getegid. -func Getegid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getegid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { c := t.Credentials() egid := c.EffectiveKGID.In(c.UserNamespace).OrOverflow() return uintptr(egid), nil, nil } // Getresgid implements the Linux syscall getresgid. -func Getresgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getresgid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { rgidAddr := args[0].Pointer() egidAddr := args[1].Pointer() sgidAddr := args[2].Pointer() @@ -97,20 +97,20 @@ func Getresgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Setuid implements the Linux syscall setuid. -func Setuid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setuid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { uid := auth.UID(args[0].Int()) return 0, nil, t.SetUID(uid) } // Setreuid implements the Linux syscall setreuid. -func Setreuid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setreuid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { ruid := auth.UID(args[0].Int()) euid := auth.UID(args[1].Int()) return 0, nil, t.SetREUID(ruid, euid) } // Setresuid implements the Linux syscall setreuid. -func Setresuid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setresuid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { ruid := auth.UID(args[0].Int()) euid := auth.UID(args[1].Int()) suid := auth.UID(args[2].Int()) @@ -118,20 +118,20 @@ func Setresuid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Setgid implements the Linux syscall setgid. -func Setgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setgid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { gid := auth.GID(args[0].Int()) return 0, nil, t.SetGID(gid) } // Setregid implements the Linux syscall setregid. -func Setregid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setregid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { rgid := auth.GID(args[0].Int()) egid := auth.GID(args[1].Int()) return 0, nil, t.SetREGID(rgid, egid) } // Setresgid implements the Linux syscall setregid. -func Setresgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setresgid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { rgid := auth.GID(args[0].Int()) egid := auth.GID(args[1].Int()) sgid := auth.GID(args[2].Int()) @@ -139,7 +139,7 @@ func Setresgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Getgroups implements the Linux syscall getgroups. -func Getgroups(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getgroups(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { size := int(args[0].Int()) if size < 0 { return 0, nil, linuxerr.EINVAL @@ -164,7 +164,7 @@ func Getgroups(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Setgroups implements the Linux syscall setgroups. -func Setgroups(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setgroups(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { size := args[0].Int() if size < 0 || size > maxNGroups { return 0, nil, linuxerr.EINVAL diff --git a/pkg/sentry/syscalls/linux/sys_inotify.go b/pkg/sentry/syscalls/linux/sys_inotify.go index b1897a9e6..45a350075 100644 --- a/pkg/sentry/syscalls/linux/sys_inotify.go +++ b/pkg/sentry/syscalls/linux/sys_inotify.go @@ -25,7 +25,7 @@ import ( const allFlags = linux.IN_NONBLOCK | linux.IN_CLOEXEC // InotifyInit1 implements the inotify_init1() syscalls. -func InotifyInit1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func InotifyInit1(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { flags := args[0].Int() if flags&^allFlags != 0 { return 0, nil, linuxerr.EINVAL @@ -49,9 +49,9 @@ func InotifyInit1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // InotifyInit implements the inotify_init() syscalls. -func InotifyInit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func InotifyInit(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { args[0].Value = 0 - return InotifyInit1(t, args) + return InotifyInit1(t, sysno, args) } // fdToInotify resolves an fd to an inotify object. If successful, the file will @@ -74,7 +74,7 @@ func fdToInotify(t *kernel.Task, fd int32) (*vfs.Inotify, *vfs.FileDescription, } // InotifyAddWatch implements the inotify_add_watch() syscall. -func InotifyAddWatch(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func InotifyAddWatch(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() mask := args[2].Uint() @@ -120,7 +120,7 @@ func InotifyAddWatch(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kern } // InotifyRmWatch implements the inotify_rm_watch() syscall. -func InotifyRmWatch(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func InotifyRmWatch(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() wd := args[1].Int() diff --git a/pkg/sentry/syscalls/linux/sys_iouring.go b/pkg/sentry/syscalls/linux/sys_iouring.go index 8e356bdb2..d5ac7c5dd 100644 --- a/pkg/sentry/syscalls/linux/sys_iouring.go +++ b/pkg/sentry/syscalls/linux/sys_iouring.go @@ -24,7 +24,7 @@ import ( ) // IOUringSetup implements linux syscall io_uring_setup(2). -func IOUringSetup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func IOUringSetup(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { if !kernel.IOUringEnabled { return 0, nil, linuxerr.ENOSYS } @@ -80,7 +80,7 @@ func IOUringSetup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // IOUringEnter implements linux syscall io_uring_enter(2). -func IOUringEnter(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func IOUringEnter(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { if !kernel.IOUringEnabled { return 0, nil, linuxerr.ENOSYS } diff --git a/pkg/sentry/syscalls/linux/sys_membarrier.go b/pkg/sentry/syscalls/linux/sys_membarrier.go index 6ceedc086..681a5ced2 100644 --- a/pkg/sentry/syscalls/linux/sys_membarrier.go +++ b/pkg/sentry/syscalls/linux/sys_membarrier.go @@ -22,7 +22,7 @@ import ( ) // Membarrier implements syscall membarrier(2). -func Membarrier(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Membarrier(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { cmd := args[0].Int() flags := args[1].Uint() @@ -97,7 +97,7 @@ func Membarrier(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy return 0, nil, nil default: // Probably a command we don't implement. - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, nil, linuxerr.EINVAL } } diff --git a/pkg/sentry/syscalls/linux/sys_mempolicy.go b/pkg/sentry/syscalls/linux/sys_mempolicy.go index 841383c47..4d68af520 100644 --- a/pkg/sentry/syscalls/linux/sys_mempolicy.go +++ b/pkg/sentry/syscalls/linux/sys_mempolicy.go @@ -102,7 +102,7 @@ func copyOutNodemask(t *kernel.Task, addr hostarch.Addr, maxnode uint32, val uin } // GetMempolicy implements the syscall get_mempolicy(2). -func GetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func GetMempolicy(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { mode := args[0].Pointer() nodemask := args[1].Pointer() maxnode := args[2].Uint() @@ -216,7 +216,7 @@ func GetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // SetMempolicy implements the syscall set_mempolicy(2). -func SetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SetMempolicy(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { modeWithFlags := linux.NumaPolicy(args[0].Int()) nodemask := args[1].Pointer() maxnode := args[2].Uint() @@ -231,7 +231,7 @@ func SetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // Mbind implements the syscall mbind(2). -func Mbind(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mbind(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].Uint64() mode := linux.NumaPolicy(args[2].Int()) diff --git a/pkg/sentry/syscalls/linux/sys_mmap.go b/pkg/sentry/syscalls/linux/sys_mmap.go index 17a884245..50ecdcaf2 100644 --- a/pkg/sentry/syscalls/linux/sys_mmap.go +++ b/pkg/sentry/syscalls/linux/sys_mmap.go @@ -28,7 +28,7 @@ import ( ) // Brk implements linux syscall brk(2). -func Brk(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Brk(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr, _ := t.MemoryManager().Brk(t, args[0].Pointer()) // "However, the actual Linux system call returns the new program break on // success. On failure, the system call returns the current break." - @@ -37,7 +37,7 @@ func Brk(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo } // Mmap implements Linux syscall mmap(2). -func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mmap(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { prot := args[2].Int() flags := args[3].Int() fd := args[4].Int() @@ -116,12 +116,12 @@ func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC } // Munmap implements linux syscall munmap(2). -func Munmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Munmap(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, t.MemoryManager().MUnmap(t, args[0].Pointer(), args[1].Uint64()) } // Mremap implements linux syscall mremap(2). -func Mremap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mremap(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { oldAddr := args[0].Pointer() oldSize := args[1].Uint64() newSize := args[2].Uint64() @@ -155,7 +155,7 @@ func Mremap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Mprotect implements linux syscall mprotect(2). -func Mprotect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mprotect(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { length := args[1].Uint64() prot := args[2].Int() err := t.MemoryManager().MProtect(args[0].Pointer(), length, hostarch.AccessType{ @@ -167,7 +167,7 @@ func Mprotect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // Madvise implements linux syscall madvise(2). -func Madvise(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Madvise(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := uint64(args[1].SizeT()) adv := args[2].Int() @@ -219,7 +219,7 @@ func Madvise(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Mincore implements the syscall mincore(2). -func Mincore(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mincore(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].SizeT() vec := args[2].Pointer() @@ -251,7 +251,7 @@ func Mincore(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Msync implements Linux syscall msync(2). -func Msync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Msync(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].SizeT() flags := args[2].Int() @@ -278,7 +278,7 @@ func Msync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Mlock implements linux syscall mlock(2). -func Mlock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mlock(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].SizeT() @@ -286,7 +286,7 @@ func Mlock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Mlock2 implements linux syscall mlock2(2). -func Mlock2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mlock2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].SizeT() flags := args[2].Int() @@ -303,7 +303,7 @@ func Mlock2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Munlock implements linux syscall munlock(2). -func Munlock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Munlock(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].SizeT() @@ -311,7 +311,7 @@ func Munlock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Mlockall implements linux syscall mlockall(2). -func Mlockall(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mlockall(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { flags := args[0].Int() if flags&^(linux.MCL_CURRENT|linux.MCL_FUTURE|linux.MCL_ONFAULT) != 0 { @@ -330,7 +330,7 @@ func Mlockall(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // Munlockall implements linux syscall munlockall(2). -func Munlockall(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Munlockall(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, t.MemoryManager().MLockAll(t, mm.MLockAllOpts{ Current: true, Future: true, diff --git a/pkg/sentry/syscalls/linux/sys_mount.go b/pkg/sentry/syscalls/linux/sys_mount.go index 1cc9c69ff..7b5a6a71d 100644 --- a/pkg/sentry/syscalls/linux/sys_mount.go +++ b/pkg/sentry/syscalls/linux/sys_mount.go @@ -26,7 +26,7 @@ import ( ) // Mount implements Linux syscall mount(2). -func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Mount(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { sourceAddr := args[0].Pointer() targetAddr := args[1].Pointer() typeAddr := args[2].Pointer() @@ -136,7 +136,7 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Umount2 implements Linux syscall umount2(2). -func Umount2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Umount2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() flags := args[1].Int() diff --git a/pkg/sentry/syscalls/linux/sys_mq.go b/pkg/sentry/syscalls/linux/sys_mq.go index d6ea28bdc..37cd123fd 100644 --- a/pkg/sentry/syscalls/linux/sys_mq.go +++ b/pkg/sentry/syscalls/linux/sys_mq.go @@ -22,7 +22,7 @@ import ( ) // MqOpen implements mq_open(2). -func MqOpen(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func MqOpen(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { nameAddr := args[0].Pointer() flag := args[1].Int() mode := args[2].ModeT() @@ -68,7 +68,7 @@ func MqOpen(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // MqUnlink implements mq_unlink(2). -func MqUnlink(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func MqUnlink(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { nameAddr := args[0].Pointer() name, err := t.CopyInString(nameAddr, mq.MaxName) if err != nil { diff --git a/pkg/sentry/syscalls/linux/sys_msgqueue.go b/pkg/sentry/syscalls/linux/sys_msgqueue.go index 60b989ee7..e2ede6cc3 100644 --- a/pkg/sentry/syscalls/linux/sys_msgqueue.go +++ b/pkg/sentry/syscalls/linux/sys_msgqueue.go @@ -26,7 +26,7 @@ import ( ) // Msgget implements msgget(2). -func Msgget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Msgget(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { key := ipc.Key(args[0].Int()) flag := args[1].Int() @@ -44,7 +44,7 @@ func Msgget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Msgsnd implements msgsnd(2). -func Msgsnd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Msgsnd(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := ipc.ID(args[0].Int()) msgAddr := args[1].Pointer() size := args[2].Int64() @@ -78,7 +78,7 @@ func Msgsnd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Msgrcv implements msgrcv(2). -func Msgrcv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Msgrcv(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := ipc.ID(args[0].Int()) msgAddr := args[1].Pointer() size := args[2].Int64() @@ -127,7 +127,7 @@ func receive(t *kernel.Task, id ipc.ID, mType int64, maxSize int64, msgCopy, wai } // Msgctl implements msgctl(2). -func Msgctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Msgctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := ipc.ID(args[0].Int()) cmd := args[1].Int() buf := args[2].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_pipe.go b/pkg/sentry/syscalls/linux/sys_pipe.go index 8ae2a75c1..fe366f298 100644 --- a/pkg/sentry/syscalls/linux/sys_pipe.go +++ b/pkg/sentry/syscalls/linux/sys_pipe.go @@ -26,13 +26,13 @@ import ( ) // Pipe implements Linux syscall pipe(2). -func Pipe(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Pipe(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() return 0, nil, pipe2(t, addr, 0) } // Pipe2 implements Linux syscall pipe2(2). -func Pipe2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Pipe2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() flags := args[1].Int() return 0, nil, pipe2(t, addr, flags) diff --git a/pkg/sentry/syscalls/linux/sys_poll.go b/pkg/sentry/syscalls/linux/sys_poll.go index 7f3662fdd..d658655aa 100644 --- a/pkg/sentry/syscalls/linux/sys_poll.go +++ b/pkg/sentry/syscalls/linux/sys_poll.go @@ -426,7 +426,7 @@ func poll(t *kernel.Task, pfdAddr hostarch.Addr, nfds uint, timeout time.Duratio } // Poll implements linux syscall poll(2). -func Poll(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Poll(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pfdAddr := args[0].Pointer() nfds := uint(args[1].Uint()) // poll(2) uses unsigned long. timeout := time.Duration(args[2].Int()) * time.Millisecond @@ -435,7 +435,7 @@ func Poll(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC } // Ppoll implements linux syscall ppoll(2). -func Ppoll(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Ppoll(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pfdAddr := args[0].Pointer() nfds := uint(args[1].Uint()) // poll(2) uses unsigned long. timespecAddr := args[2].Pointer() @@ -473,7 +473,7 @@ func Ppoll(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Select implements linux syscall select(2). -func Select(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Select(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { nfds := int(args[0].Int()) // select(2) uses an int. readFDs := args[1].Pointer() writeFDs := args[2].Pointer() @@ -509,7 +509,7 @@ type sigSetWithSize struct { } // Pselect6 implements linux syscall pselect6(2). -func Pselect6(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Pselect6(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { nfds := int(args[0].Int()) // select(2) uses an int. readFDs := args[1].Pointer() writeFDs := args[2].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_prctl.go b/pkg/sentry/syscalls/linux/sys_prctl.go index 4464ef227..cd4b56a75 100644 --- a/pkg/sentry/syscalls/linux/sys_prctl.go +++ b/pkg/sentry/syscalls/linux/sys_prctl.go @@ -30,7 +30,7 @@ import ( // Prctl implements linux syscall prctl(2). // It has a list of subfunctions which operate on the process. The arguments are // all based on each subfunction. -func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Prctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { option := args[0].Int() switch option { @@ -155,7 +155,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall linux.PR_SET_MM_ENV_START, linux.PR_SET_MM_ENV_END: - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) fallthrough default: return 0, nil, linuxerr.EINVAL @@ -234,7 +234,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall return 0, nil, nil } - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, nil, linuxerr.EINVAL case linux.PR_GET_TIMING, @@ -254,7 +254,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall linux.PR_MPX_ENABLE_MANAGEMENT, linux.PR_MPX_DISABLE_MANAGEMENT: - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) fallthrough default: return 0, nil, linuxerr.EINVAL diff --git a/pkg/sentry/syscalls/linux/sys_process_vm.go b/pkg/sentry/syscalls/linux/sys_process_vm.go index 748036350..ff4ce0e71 100644 --- a/pkg/sentry/syscalls/linux/sys_process_vm.go +++ b/pkg/sentry/syscalls/linux/sys_process_vm.go @@ -32,12 +32,12 @@ const ( ) // ProcessVMReadv implements process_vm_readv(2). -func ProcessVMReadv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ProcessVMReadv(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return processVMRW(t, args, false /*isWrite*/) } // ProcessVMWritev implements process_vm_writev(2). -func ProcessVMWritev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ProcessVMWritev(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return processVMRW(t, args, true /*isWrite*/) } diff --git a/pkg/sentry/syscalls/linux/sys_random.go b/pkg/sentry/syscalls/linux/sys_random.go index f86e87bc7..da5b527dd 100644 --- a/pkg/sentry/syscalls/linux/sys_random.go +++ b/pkg/sentry/syscalls/linux/sys_random.go @@ -39,7 +39,7 @@ const ( // possible. The urandom pool is also expected to have plenty of entropy, thus // the GRND_RANDOM flag is ignored. The GRND_NONBLOCK flag does not apply, as // the pool will already be initialized. -func GetRandom(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func GetRandom(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].SizeT() flags := args[2].Int() diff --git a/pkg/sentry/syscalls/linux/sys_read_write.go b/pkg/sentry/syscalls/linux/sys_read_write.go index 1e9d12d89..d014ae87b 100644 --- a/pkg/sentry/syscalls/linux/sys_read_write.go +++ b/pkg/sentry/syscalls/linux/sys_read_write.go @@ -34,7 +34,7 @@ const ( ) // Read implements Linux syscall read(2). -func Read(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Read(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() size := args[2].SizeT() @@ -65,7 +65,7 @@ func Read(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC } // Readv implements Linux syscall readv(2). -func Readv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Readv(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() iovcnt := int(args[2].Int()) @@ -133,7 +133,7 @@ func read(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, opt } // Pread64 implements Linux syscall pread64(2). -func Pread64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Pread64(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() size := args[2].SizeT() @@ -170,7 +170,7 @@ func Pread64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Preadv implements Linux syscall preadv(2). -func Preadv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Preadv(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() iovcnt := int(args[2].Int()) @@ -201,7 +201,7 @@ func Preadv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Preadv2 implements Linux syscall preadv2(2). -func Preadv2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Preadv2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // While the glibc signature is // preadv2(int fd, struct iovec* iov, int iov_cnt, off_t offset, int flags) // the actual syscall @@ -288,7 +288,7 @@ func pread(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, of } // Write implements Linux syscall write(2). -func Write(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Write(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() size := args[2].SizeT() @@ -319,7 +319,7 @@ func Write(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Writev implements Linux syscall writev(2). -func Writev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Writev(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() iovcnt := int(args[2].Int()) @@ -386,7 +386,7 @@ func write(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, op } // Pwrite64 implements Linux syscall pwrite64(2). -func Pwrite64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Pwrite64(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() size := args[2].SizeT() @@ -423,7 +423,7 @@ func Pwrite64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // Pwritev implements Linux syscall pwritev(2). -func Pwritev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Pwritev(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() iovcnt := int(args[2].Int()) @@ -454,7 +454,7 @@ func Pwritev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Pwritev2 implements Linux syscall pwritev2(2). -func Pwritev2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Pwritev2(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // While the glibc signature is // pwritev2(int fd, struct iovec* iov, int iov_cnt, off_t offset, int flags) // the actual syscall @@ -559,7 +559,7 @@ func blockPolicy(t *kernel.Task, file *vfs.FileDescription) (allowBlock bool, de } // Lseek implements Linux syscall lseek(2). -func Lseek(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Lseek(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() offset := args[1].Int64() whence := args[2].Int() @@ -575,7 +575,7 @@ func Lseek(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Readahead implements readahead(2). -func Readahead(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Readahead(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() offset := args[1].Int64() size := args[2].SizeT() diff --git a/pkg/sentry/syscalls/linux/sys_rlimit.go b/pkg/sentry/syscalls/linux/sys_rlimit.go index 7210333d2..293a5c974 100644 --- a/pkg/sentry/syscalls/linux/sys_rlimit.go +++ b/pkg/sentry/syscalls/linux/sys_rlimit.go @@ -125,7 +125,7 @@ func prlimit64(t *kernel.Task, resource limits.LimitType, newLim *limits.Limit) } // Getrlimit implements linux syscall getrlimit(2). -func Getrlimit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getrlimit(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { resource, ok := limits.FromLinuxResource[int(args[0].Int())] if !ok { // Return err; unknown limit. @@ -146,7 +146,7 @@ func Getrlimit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Setrlimit implements linux syscall setrlimit(2). -func Setrlimit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setrlimit(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { resource, ok := limits.FromLinuxResource[int(args[0].Int())] if !ok { // Return err; unknown limit. @@ -165,7 +165,7 @@ func Setrlimit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Prlimit64 implements linux syscall prlimit64(2). -func Prlimit64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Prlimit64(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tid := kernel.ThreadID(args[0].Int()) resource, ok := limits.FromLinuxResource[int(args[1].Int())] if !ok { diff --git a/pkg/sentry/syscalls/linux/sys_rseq.go b/pkg/sentry/syscalls/linux/sys_rseq.go index 8328a3742..33e7ddf2e 100644 --- a/pkg/sentry/syscalls/linux/sys_rseq.go +++ b/pkg/sentry/syscalls/linux/sys_rseq.go @@ -22,7 +22,7 @@ import ( ) // RSeq implements syscall rseq(2). -func RSeq(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RSeq(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() length := args[1].Uint() flags := args[2].Int() @@ -31,7 +31,7 @@ func RSeq(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC if !t.RSeqAvailable() { // Event for applications that want rseq on a configuration // that doesn't support them. - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, nil, linuxerr.ENOSYS } diff --git a/pkg/sentry/syscalls/linux/sys_rusage.go b/pkg/sentry/syscalls/linux/sys_rusage.go index c1bdf4660..88e930b86 100644 --- a/pkg/sentry/syscalls/linux/sys_rusage.go +++ b/pkg/sentry/syscalls/linux/sys_rusage.go @@ -72,7 +72,7 @@ func getrusage(t *kernel.Task, which int32) linux.Rusage { // * long ru_nsignals; /* signals received */ // y long ru_nvcsw; /* voluntary context switches */ // y long ru_nivcsw; /* involuntary context switches */ -func Getrusage(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getrusage(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { which := args[0].Int() addr := args[1].Pointer() @@ -86,7 +86,7 @@ func Getrusage(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Times implements linux syscall times(2). -func Times(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Times(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() // Calculate the ticks first, and figure out if any additional work is diff --git a/pkg/sentry/syscalls/linux/sys_sched.go b/pkg/sentry/syscalls/linux/sys_sched.go index 59c7a4b22..5dbb09bb1 100644 --- a/pkg/sentry/syscalls/linux/sys_sched.go +++ b/pkg/sentry/syscalls/linux/sys_sched.go @@ -34,7 +34,7 @@ type SchedParam struct { } // SchedGetparam implements linux syscall sched_getparam(2). -func SchedGetparam(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SchedGetparam(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pid := args[0].Int() param := args[1].Pointer() if param == 0 { @@ -55,7 +55,7 @@ func SchedGetparam(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel } // SchedGetscheduler implements linux syscall sched_getscheduler(2). -func SchedGetscheduler(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SchedGetscheduler(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pid := args[0].Int() if pid < 0 { return 0, nil, linuxerr.EINVAL @@ -67,7 +67,7 @@ func SchedGetscheduler(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ke } // SchedSetscheduler implements linux syscall sched_setscheduler(2). -func SchedSetscheduler(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SchedSetscheduler(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pid := args[0].Int() policy := args[1].Int() param := args[2].Pointer() @@ -91,11 +91,11 @@ func SchedSetscheduler(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ke } // SchedGetPriorityMax implements linux syscall sched_get_priority_max(2). -func SchedGetPriorityMax(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SchedGetPriorityMax(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return onlyPriority, nil, nil } // SchedGetPriorityMin implements linux syscall sched_get_priority_min(2). -func SchedGetPriorityMin(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SchedGetPriorityMin(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return onlyPriority, nil, nil } diff --git a/pkg/sentry/syscalls/linux/sys_seccomp.go b/pkg/sentry/syscalls/linux/sys_seccomp.go index b0dc84b8d..cf824b407 100644 --- a/pkg/sentry/syscalls/linux/sys_seccomp.go +++ b/pkg/sentry/syscalls/linux/sys_seccomp.go @@ -73,6 +73,6 @@ func seccomp(t *kernel.Task, mode, flags uint64, addr hostarch.Addr) error { } // Seccomp implements linux syscall seccomp(2). -func Seccomp(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Seccomp(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, seccomp(t, args[0].Uint64(), args[1].Uint64(), args[2].Pointer()) } diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go index 5a119b21c..4ec7eab84 100644 --- a/pkg/sentry/syscalls/linux/sys_sem.go +++ b/pkg/sentry/syscalls/linux/sys_sem.go @@ -31,7 +31,7 @@ import ( const opsMax = 500 // SEMOPM // Semget handles: semget(key_t key, int nsems, int semflg) -func Semget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Semget(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { key := ipc.Key(args[0].Int()) nsems := args[1].Int() flag := args[2].Int() @@ -50,10 +50,10 @@ func Semget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Semtimedop handles: semop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout) -func Semtimedop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Semtimedop(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // If the timeout argument is NULL, then semtimedop() behaves exactly like semop(). if args[3].Pointer() == 0 { - return Semop(t, args) + return Semop(t, sysno, args) } id := ipc.ID(args[0].Int()) @@ -90,7 +90,7 @@ func Semtimedop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy } // Semop handles: semop(int semid, struct sembuf *sops, size_t nsops) -func Semop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Semop(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := ipc.ID(args[0].Int()) sembufAddr := args[1].Pointer() nsops := args[2].SizeT() @@ -130,7 +130,7 @@ func semTimedOp(t *kernel.Task, id ipc.ID, ops []linux.Sembuf, haveTimeout bool, } // Semctl handles: semctl(int semid, int semnum, int cmd, ...) -func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Semctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := ipc.ID(args[0].Int()) num := args[1].Int() cmd := args[2].Int() diff --git a/pkg/sentry/syscalls/linux/sys_shm.go b/pkg/sentry/syscalls/linux/sys_shm.go index 840540506..080190c92 100644 --- a/pkg/sentry/syscalls/linux/sys_shm.go +++ b/pkg/sentry/syscalls/linux/sys_shm.go @@ -24,7 +24,7 @@ import ( ) // Shmget implements shmget(2). -func Shmget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Shmget(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { key := ipc.Key(args[0].Int()) size := uint64(args[1].SizeT()) flag := args[2].Int() @@ -58,7 +58,7 @@ func findSegment(t *kernel.Task, id ipc.ID) (*shm.Shm, error) { } // Shmat implements shmat(2). -func Shmat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Shmat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := ipc.ID(args[0].Int()) addr := args[1].Pointer() flag := args[2].Int() @@ -82,14 +82,14 @@ func Shmat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Shmdt implements shmdt(2). -func Shmdt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Shmdt(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() err := t.MemoryManager().DetachShm(t, addr) return 0, nil, err } // Shmctl implements shmctl(2). -func Shmctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Shmctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { id := ipc.ID(args[0].Int()) cmd := args[1].Int() buf := args[2].Pointer() @@ -152,7 +152,7 @@ func Shmctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal // We currently do not support memory locking anywhere. // mlock(2)/munlock(2) are currently stubbed out as no-ops so do the // same here. - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, nil, nil default: diff --git a/pkg/sentry/syscalls/linux/sys_signal.go b/pkg/sentry/syscalls/linux/sys_signal.go index 9306d68e1..733c33be7 100644 --- a/pkg/sentry/syscalls/linux/sys_signal.go +++ b/pkg/sentry/syscalls/linux/sys_signal.go @@ -65,7 +65,7 @@ func mayKill(t *kernel.Task, target *kernel.Task, sig linux.Signal) bool { } // Kill implements linux syscall kill(2). -func Kill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Kill(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pid := kernel.ThreadID(args[0].Int()) sig := linux.Signal(args[1].Int()) @@ -195,7 +195,7 @@ func tkillSigInfo(sender, receiver *kernel.Task, sig linux.Signal) *linux.Signal } // Tkill implements linux syscall tkill(2). -func Tkill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Tkill(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tid := kernel.ThreadID(args[0].Int()) sig := linux.Signal(args[1].Int()) @@ -217,7 +217,7 @@ func Tkill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Tgkill implements linux syscall tgkill(2). -func Tgkill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Tgkill(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tgid := kernel.ThreadID(args[0].Int()) tid := kernel.ThreadID(args[1].Int()) sig := linux.Signal(args[2].Int()) @@ -241,7 +241,7 @@ func Tgkill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // RtSigaction implements linux syscall rt_sigaction(2). -func RtSigaction(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RtSigaction(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { sig := linux.Signal(args[0].Int()) newactarg := args[1].Pointer() oldactarg := args[2].Pointer() @@ -272,19 +272,19 @@ func RtSigaction(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S } // Sigreturn implements linux syscall sigreturn(2). -func Sigreturn(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Sigreturn(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { ctrl, err := t.SignalReturn(false) return 0, ctrl, err } // RtSigreturn implements linux syscall rt_sigreturn(2). -func RtSigreturn(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RtSigreturn(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { ctrl, err := t.SignalReturn(true) return 0, ctrl, err } // RtSigprocmask implements linux syscall rt_sigprocmask(2). -func RtSigprocmask(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RtSigprocmask(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { how := args[0].Int() setaddr := args[1].Pointer() oldaddr := args[2].Pointer() @@ -319,7 +319,7 @@ func RtSigprocmask(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel } // Sigaltstack implements linux syscall sigaltstack(2). -func Sigaltstack(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Sigaltstack(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { setaddr := args[0].Pointer() oldaddr := args[1].Pointer() @@ -328,12 +328,12 @@ func Sigaltstack(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S } // Pause implements linux syscall pause(2). -func Pause(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Pause(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, linuxerr.ConvertIntr(t.Block(nil), linuxerr.ERESTARTNOHAND) } // RtSigpending implements linux syscall rt_sigpending(2). -func RtSigpending(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RtSigpending(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() pending := t.PendingSignals() _, err := pending.CopyOut(t, addr) @@ -341,7 +341,7 @@ func RtSigpending(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // RtSigtimedwait implements linux syscall rt_sigtimedwait(2). -func RtSigtimedwait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RtSigtimedwait(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { sigset := args[0].Pointer() siginfo := args[1].Pointer() timespec := args[2].Pointer() @@ -381,7 +381,7 @@ func RtSigtimedwait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne } // RtSigqueueinfo implements linux syscall rt_sigqueueinfo(2). -func RtSigqueueinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RtSigqueueinfo(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pid := kernel.ThreadID(args[0].Int()) sig := linux.Signal(args[1].Int()) infoAddr := args[2].Pointer() @@ -422,7 +422,7 @@ func RtSigqueueinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne } // RtTgsigqueueinfo implements linux syscall rt_tgsigqueueinfo(2). -func RtTgsigqueueinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RtTgsigqueueinfo(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tgid := kernel.ThreadID(args[0].Int()) tid := kernel.ThreadID(args[1].Int()) sig := linux.Signal(args[2].Int()) @@ -461,7 +461,7 @@ func RtTgsigqueueinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ker } // RtSigsuspend implements linux syscall rt_sigsuspend(2). -func RtSigsuspend(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RtSigsuspend(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { sigset := args[0].Pointer() // Copy in the signal mask. @@ -481,7 +481,7 @@ func RtSigsuspend(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // RestartSyscall implements the linux syscall restart_syscall(2). -func RestartSyscall(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RestartSyscall(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { if r := t.SyscallRestartBlock(); r != nil { n, err := r.Restart(t) return n, nil, err @@ -555,7 +555,7 @@ func sharedSignalfd(t *kernel.Task, fd int32, sigset hostarch.Addr, sigsetsize u } // Signalfd implements the linux syscall signalfd(2). -func Signalfd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Signalfd(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() sigset := args[1].Pointer() sigsetsize := args[2].SizeT() @@ -563,7 +563,7 @@ func Signalfd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // Signalfd4 implements the linux syscall signalfd4(2). -func Signalfd4(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Signalfd4(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() sigset := args[1].Pointer() sigsetsize := args[2].SizeT() diff --git a/pkg/sentry/syscalls/linux/sys_socket.go b/pkg/sentry/syscalls/linux/sys_socket.go index bd6b609d1..7dc814267 100644 --- a/pkg/sentry/syscalls/linux/sys_socket.go +++ b/pkg/sentry/syscalls/linux/sys_socket.go @@ -169,7 +169,7 @@ func writeAddress(t *kernel.Task, addr linux.SockAddr, addrLen uint32, addrPtr h } // Socket implements the linux syscall socket(2). -func Socket(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Socket(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { domain := int(args[0].Int()) stype := args[1].Int() protocol := int(args[2].Int()) @@ -201,7 +201,7 @@ func Socket(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // SocketPair implements the linux syscall socketpair(2). -func SocketPair(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SocketPair(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { domain := int(args[0].Int()) stype := args[1].Int() protocol := int(args[2].Int()) @@ -251,7 +251,7 @@ func SocketPair(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy } // Connect implements the linux syscall connect(2). -func Connect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Connect(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() addrlen := args[2].Uint() @@ -320,7 +320,7 @@ func accept(t *kernel.Task, fd int32, addr hostarch.Addr, addrLen hostarch.Addr, } // Accept4 implements the linux syscall accept4(2). -func Accept4(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Accept4(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() addrlen := args[2].Pointer() @@ -331,7 +331,7 @@ func Accept4(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Accept implements the linux syscall accept(2). -func Accept(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Accept(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() addrlen := args[2].Pointer() @@ -341,7 +341,7 @@ func Accept(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Bind implements the linux syscall bind(2). -func Bind(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Bind(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() addrlen := args[2].Uint() @@ -369,7 +369,7 @@ func Bind(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC } // Listen implements the linux syscall listen(2). -func Listen(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Listen(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() backlog := args[1].Uint() @@ -407,7 +407,7 @@ func Listen(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Shutdown implements the linux syscall shutdown(2). -func Shutdown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Shutdown(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() how := args[1].Int() @@ -435,7 +435,7 @@ func Shutdown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // GetSockOpt implements the linux syscall getsockopt(2). -func GetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func GetSockOpt(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() level := args[1].Int() name := args[2].Int() @@ -516,7 +516,7 @@ func getSockOpt(t *kernel.Task, s socket.Socket, level, name int, optValAddr hos // SetSockOpt implements the linux syscall setsockopt(2). // // Note that unlike Linux, enabling SO_PASSCRED does not autobind the socket. -func SetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SetSockOpt(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() level := args[1].Int() name := args[2].Int() @@ -556,7 +556,7 @@ func SetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy } // GetSockName implements the linux syscall getsockname(2). -func GetSockName(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func GetSockName(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() addrlen := args[2].Pointer() @@ -584,7 +584,7 @@ func GetSockName(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S } // GetPeerName implements the linux syscall getpeername(2). -func GetPeerName(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func GetPeerName(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() addrlen := args[2].Pointer() @@ -612,7 +612,7 @@ func GetPeerName(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S } // RecvMsg implements the linux syscall recvmsg(2). -func RecvMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RecvMsg(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() msgPtr := args[1].Pointer() flags := args[2].Int() @@ -658,7 +658,7 @@ func RecvMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // RecvMMsg implements the linux syscall recvmmsg(2). -func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RecvMMsg(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() msgPtr := args[1].Pointer() vlen := args[2].Uint() @@ -937,7 +937,7 @@ func recvFrom(t *kernel.Task, fd int32, bufPtr hostarch.Addr, bufLen uint64, fla } // RecvFrom implements the linux syscall recvfrom(2). -func RecvFrom(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RecvFrom(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() bufPtr := args[1].Pointer() bufLen := args[2].Uint64() @@ -950,7 +950,7 @@ func RecvFrom(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc } // SendMsg implements the linux syscall sendmsg(2). -func SendMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SendMsg(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() msgPtr := args[1].Pointer() flags := args[2].Int() @@ -987,7 +987,7 @@ func SendMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // SendMMsg implements the linux syscall sendmmsg(2). -func SendMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SendMMsg(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() msgPtr := args[1].Pointer() vlen := args[2].Uint() @@ -1175,7 +1175,7 @@ func sendTo(t *kernel.Task, fd int32, bufPtr hostarch.Addr, bufLen uint64, flags } // SendTo implements the linux syscall sendto(2). -func SendTo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SendTo(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() bufPtr := args[1].Pointer() bufLen := args[2].Uint64() diff --git a/pkg/sentry/syscalls/linux/sys_splice.go b/pkg/sentry/syscalls/linux/sys_splice.go index ef43fc96d..34313cd72 100644 --- a/pkg/sentry/syscalls/linux/sys_splice.go +++ b/pkg/sentry/syscalls/linux/sys_splice.go @@ -30,7 +30,7 @@ import ( ) // Splice implements Linux syscall splice(2). -func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Splice(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { inFD := args[0].Int() inOffsetPtr := args[1].Pointer() outFD := args[2].Int() @@ -178,7 +178,7 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Tee implements Linux syscall tee(2). -func Tee(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Tee(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { inFD := args[0].Int() outFD := args[1].Int() count := int64(args[2].SizeT()) @@ -267,7 +267,7 @@ func Tee(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo } // Sendfile implements linux system call sendfile(2). -func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Sendfile(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { outFD := args[0].Int() inFD := args[1].Int() offsetAddr := args[2].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_stat.go b/pkg/sentry/syscalls/linux/sys_stat.go index 2d3fbabf0..4386debe6 100644 --- a/pkg/sentry/syscalls/linux/sys_stat.go +++ b/pkg/sentry/syscalls/linux/sys_stat.go @@ -27,21 +27,21 @@ import ( ) // Stat implements Linux syscall stat(2). -func Stat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Stat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() statAddr := args[1].Pointer() return 0, nil, fstatat(t, linux.AT_FDCWD, pathAddr, statAddr, 0 /* flags */) } // Lstat implements Linux syscall lstat(2). -func Lstat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Lstat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() statAddr := args[1].Pointer() return 0, nil, fstatat(t, linux.AT_FDCWD, pathAddr, statAddr, linux.AT_SYMLINK_NOFOLLOW) } // Newfstatat implements Linux syscall newfstatat, which backs fstatat(2). -func Newfstatat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Newfstatat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() statAddr := args[2].Pointer() @@ -123,7 +123,7 @@ func timespecFromStatxTimestamp(sxts linux.StatxTimestamp) linux.Timespec { } // Fstat implements Linux syscall fstat(2). -func Fstat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fstat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() statAddr := args[1].Pointer() @@ -146,7 +146,7 @@ func Fstat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Statx implements Linux syscall statx(2). -func Statx(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Statx(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() flags := args[2].Int() @@ -236,7 +236,7 @@ func userifyStatx(t *kernel.Task, statx *linux.Statx) { } // Statfs implements Linux syscall statfs(2). -func Statfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Statfs(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() bufAddr := args[1].Pointer() @@ -259,7 +259,7 @@ func Statfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Fstatfs implements Linux syscall fstatfs(2). -func Fstatfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fstatfs(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() bufAddr := args[1].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_sync.go b/pkg/sentry/syscalls/linux/sys_sync.go index 3b0fa158a..1a8c0c96c 100644 --- a/pkg/sentry/syscalls/linux/sys_sync.go +++ b/pkg/sentry/syscalls/linux/sys_sync.go @@ -22,12 +22,12 @@ import ( ) // Sync implements Linux syscall sync(2). -func Sync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Sync(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, t.Kernel().VFS().SyncAllFilesystems(t) } // Syncfs implements Linux syscall syncfs(2). -func Syncfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Syncfs(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() file := t.GetFile(fd) @@ -44,7 +44,7 @@ func Syncfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Fsync implements Linux syscall fsync(2). -func Fsync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fsync(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() file := t.GetFile(fd) @@ -57,13 +57,13 @@ func Fsync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Fdatasync implements Linux syscall fdatasync(2). -func Fdatasync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fdatasync(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // TODO(gvisor.dev/issue/1897): Avoid writeback of unnecessary metadata. - return Fsync(t, args) + return Fsync(t, sysno, args) } // SyncFileRange implements Linux syscall sync_file_range(2). -func SyncFileRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SyncFileRange(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() offset := args[1].Int64() nbytes := args[2].Int64() @@ -106,7 +106,7 @@ func SyncFileRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel if flags&linux.SYNC_FILE_RANGE_WAIT_BEFORE != 0 && flags&linux.SYNC_FILE_RANGE_WAIT_AFTER == 0 { - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, nil, linuxerr.ENOSYS } diff --git a/pkg/sentry/syscalls/linux/sys_sysinfo.go b/pkg/sentry/syscalls/linux/sys_sysinfo.go index db3d924d9..90b38bcc9 100644 --- a/pkg/sentry/syscalls/linux/sys_sysinfo.go +++ b/pkg/sentry/syscalls/linux/sys_sysinfo.go @@ -22,7 +22,7 @@ import ( ) // Sysinfo implements Linux syscall sysinfo(2). -func Sysinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Sysinfo(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() mf := t.Kernel().MemoryFile() diff --git a/pkg/sentry/syscalls/linux/sys_syslog.go b/pkg/sentry/syscalls/linux/sys_syslog.go index 15acb2b8b..543a2db83 100644 --- a/pkg/sentry/syscalls/linux/sys_syslog.go +++ b/pkg/sentry/syscalls/linux/sys_syslog.go @@ -32,7 +32,7 @@ const logBufLen = 1 << 17 // // Only the unpriviledged commands are implemented, allowing applications to // read a fun dmesg. -func Syslog(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Syslog(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { command := args[0].Int() buf := args[1].Pointer() size := int(args[2].Int()) diff --git a/pkg/sentry/syscalls/linux/sys_thread.go b/pkg/sentry/syscalls/linux/sys_thread.go index b978179da..9b448821f 100644 --- a/pkg/sentry/syscalls/linux/sys_thread.go +++ b/pkg/sentry/syscalls/linux/sys_thread.go @@ -44,7 +44,7 @@ var ( ) // Getppid implements linux syscall getppid(2). -func Getppid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getppid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { parent := t.Parent() if parent == nil { return 0, nil, nil @@ -53,17 +53,17 @@ func Getppid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Getpid implements linux syscall getpid(2). -func Getpid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getpid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return uintptr(t.ThreadGroup().ID()), nil, nil } // Gettid implements linux syscall gettid(2). -func Gettid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Gettid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return uintptr(t.ThreadID()), nil, nil } // Execve implements linux syscall execve(2). -func Execve(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Execve(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathnameAddr := args[0].Pointer() argvAddr := args[1].Pointer() envvAddr := args[2].Pointer() @@ -71,7 +71,7 @@ func Execve(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Execveat implements linux syscall execveat(2). -func Execveat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Execveat(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathnameAddr := args[1].Pointer() argvAddr := args[2].Pointer() @@ -189,14 +189,14 @@ func execveat(t *kernel.Task, dirfd int32, pathnameAddr, argvAddr, envvAddr host } // Exit implements linux syscall exit(2). -func Exit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Exit(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { status := args[0].Int() t.PrepareExit(linux.WaitStatusExit(status & 0xff)) return 0, kernel.CtrlDoExit, nil } // ExitGroup implements linux syscall exit_group(2). -func ExitGroup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ExitGroup(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { status := args[0].Int() t.PrepareGroupExit(linux.WaitStatusExit(status & 0xff)) return 0, kernel.CtrlDoExit, nil @@ -218,14 +218,14 @@ func clone(t *kernel.Task, flags int, stack hostarch.Addr, parentTID hostarch.Ad } // Fork implements Linux syscall fork(2). -func Fork(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fork(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // "A call to fork() is equivalent to a call to clone(2) specifying flags // as just SIGCHLD." - fork(2) return clone(t, int(linux.SIGCHLD), 0, 0, 0, 0) } // Vfork implements Linux syscall vfork(2). -func Vfork(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Vfork(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // """ // A call to vfork() is equivalent to calling clone(2) with flags specified as: // @@ -315,7 +315,7 @@ func wait4(t *kernel.Task, pid int, statusAddr hostarch.Addr, options int, rusag } // Wait4 implements linux syscall wait4(2). -func Wait4(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Wait4(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pid := int(args[0].Int()) statusAddr := args[1].Pointer() options := int(args[2].Uint()) @@ -326,7 +326,7 @@ func Wait4(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // WaitPid implements linux syscall waitpid(2). -func WaitPid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func WaitPid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pid := int(args[0].Int()) statusAddr := args[1].Pointer() options := int(args[2].Uint()) @@ -336,7 +336,7 @@ func WaitPid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Waitid implements linux syscall waitid(2). -func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Waitid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { idtype := args[0].Int() id := args[1].Int() infop := args[2].Pointer() @@ -435,7 +435,7 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // SetTidAddress implements linux syscall set_tid_address(2). -func SetTidAddress(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SetTidAddress(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() // Always succeed, return caller's tid. @@ -444,7 +444,7 @@ func SetTidAddress(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel } // Unshare implements linux syscall unshare(2). -func Unshare(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Unshare(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { flags := args[0].Int() // "CLONE_NEWPID automatically implies CLONE_THREAD as well." - unshare(2) if flags&linux.CLONE_NEWPID != 0 { @@ -459,13 +459,13 @@ func Unshare(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // SchedYield implements linux syscall sched_yield(2). -func SchedYield(t *kernel.Task, _ arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SchedYield(t *kernel.Task, sysno uintptr, _ arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { t.Yield() return 0, nil, nil } // SchedSetaffinity implements linux syscall sched_setaffinity(2). -func SchedSetaffinity(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SchedSetaffinity(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tid := args[0].Int() size := args[1].SizeT() maskAddr := args[2].Pointer() @@ -491,7 +491,7 @@ func SchedSetaffinity(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ker } // SchedGetaffinity implements linux syscall sched_getaffinity(2). -func SchedGetaffinity(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SchedGetaffinity(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tid := args[0].Int() size := args[1].SizeT() maskAddr := args[2].Pointer() @@ -528,7 +528,7 @@ func SchedGetaffinity(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ker } // Getcpu implements linux syscall getcpu(2). -func Getcpu(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getcpu(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { cpu := args[0].Pointer() node := args[1].Pointer() // third argument to this system call is nowadays unused. @@ -550,7 +550,7 @@ func Getcpu(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Setpgid implements the linux syscall setpgid(2). -func Setpgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setpgid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { // Note that throughout this function, pgid is interpreted with respect // to t's namespace, not with respect to the selected ThreadGroup's // namespace (which may be different). @@ -613,15 +613,15 @@ func Setpgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Getpgrp implements the linux syscall getpgrp(2). -func Getpgrp(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getpgrp(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return uintptr(t.PIDNamespace().IDOfProcessGroup(t.ThreadGroup().ProcessGroup())), nil, nil } // Getpgid implements the linux syscall getpgid(2). -func Getpgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getpgid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tid := kernel.ThreadID(args[0].Int()) if tid == 0 { - return Getpgrp(t, args) + return Getpgrp(t, sysno, args) } target := t.PIDNamespace().TaskWithID(tid) @@ -633,12 +633,12 @@ func Getpgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Setsid implements the linux syscall setsid(2). -func Setsid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setsid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, t.ThreadGroup().CreateSession() } // Getsid implements the linux syscall getsid(2). -func Getsid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getsid(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tid := kernel.ThreadID(args[0].Int()) if tid == 0 { return uintptr(t.PIDNamespace().IDOfSession(t.ThreadGroup().Session())), nil, nil @@ -655,7 +655,7 @@ func Getsid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal // Getpriority pretends to implement the linux syscall getpriority(2). // // This is a stub; real priorities require a full scheduler. -func Getpriority(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getpriority(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { which := args[0].Int() who := kernel.ThreadID(args[1].Int()) @@ -691,7 +691,7 @@ func Getpriority(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S // Setpriority pretends to implement the linux syscall setpriority(2). // // This is a stub; real priorities require a full scheduler. -func Setpriority(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setpriority(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { which := args[0].Int() who := kernel.ThreadID(args[1].Int()) niceval := int(args[2].Int()) @@ -733,7 +733,7 @@ func Setpriority(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S } // Ptrace implements linux system call ptrace(2). -func Ptrace(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Ptrace(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { req := args[0].Int64() pid := kernel.ThreadID(args[1].Int()) addr := args[2].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_time.go b/pkg/sentry/syscalls/linux/sys_time.go index 11f4384d6..8c2d5eafb 100644 --- a/pkg/sentry/syscalls/linux/sys_time.go +++ b/pkg/sentry/syscalls/linux/sys_time.go @@ -66,7 +66,7 @@ func targetTask(t *kernel.Task, c int32) *kernel.Task { } // ClockGetres implements linux syscall clock_getres(2). -func ClockGetres(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ClockGetres(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { clockID := int32(args[0].Int()) addr := args[1].Pointer() r := linux.Timespec{ @@ -143,7 +143,7 @@ func getClock(t *kernel.Task, clockID int32) (ktime.Clock, error) { } // ClockGettime implements linux syscall clock_gettime(2). -func ClockGettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ClockGettime(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { clockID := int32(args[0].Int()) addr := args[1].Pointer() @@ -156,12 +156,12 @@ func ClockGettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // ClockSettime implements linux syscall clock_settime(2). -func ClockSettime(*kernel.Task, arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ClockSettime(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, linuxerr.EPERM } // Time implements linux syscall time(2). -func Time(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Time(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() r := t.Kernel().RealtimeClock().Now().TimeT() @@ -176,7 +176,7 @@ func Time(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC } // Nanosleep implements linux syscall Nanosleep(2). -func Nanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Nanosleep(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() rem := args[1].Pointer() @@ -197,7 +197,7 @@ func Nanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // ClockNanosleep implements linux syscall clock_nanosleep(2). -func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ClockNanosleep(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { clockID := int32(args[0].Int()) flags := args[1].Int() addr := args[2].Pointer() @@ -305,7 +305,7 @@ func (n *clockNanosleepRestartBlock) Restart(t *kernel.Task) (uintptr, error) { } // Gettimeofday implements linux syscall gettimeofday(2). -func Gettimeofday(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Gettimeofday(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { tv := args[0].Pointer() tz := args[1].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_timer.go b/pkg/sentry/syscalls/linux/sys_timer.go index d39a0a6f5..1b8e5a47c 100644 --- a/pkg/sentry/syscalls/linux/sys_timer.go +++ b/pkg/sentry/syscalls/linux/sys_timer.go @@ -26,7 +26,7 @@ import ( const nsecPerSec = int64(time.Second) // Getitimer implements linux syscall getitimer(2). -func Getitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Getitimer(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { if t.Arch().Width() != 8 { // Definition of linux.ItimerVal assumes 64-bit architecture. return 0, nil, linuxerr.ENOSYS @@ -48,7 +48,7 @@ func Getitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Setitimer implements linux syscall setitimer(2). -func Setitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setitimer(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { if t.Arch().Width() != 8 { // Definition of linux.ItimerVal assumes 64-bit architecture. return 0, nil, linuxerr.ENOSYS @@ -81,7 +81,7 @@ func Setitimer(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // Alarm implements linux syscall alarm(2). -func Alarm(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Alarm(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { duration := time.Duration(args[0].Uint()) * time.Second olditv, err := t.Setitimer(linux.ITIMER_REAL, linux.ItimerVal{ @@ -100,7 +100,7 @@ func Alarm(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // TimerCreate implements linux syscall timer_create(2). -func TimerCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func TimerCreate(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { clockID := args[0].Int() sevp := args[1].Pointer() timerIDp := args[2].Pointer() @@ -132,7 +132,7 @@ func TimerCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S } // TimerSettime implements linux syscall timer_settime(2). -func TimerSettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func TimerSettime(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { timerID := linux.TimerID(args[0].Value) flags := args[1].Int() newValAddr := args[2].Pointer() @@ -154,7 +154,7 @@ func TimerSettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // TimerGettime implements linux syscall timer_gettime(2). -func TimerGettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func TimerGettime(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { timerID := linux.TimerID(args[0].Value) curValAddr := args[1].Pointer() @@ -167,7 +167,7 @@ func TimerGettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel. } // TimerGetoverrun implements linux syscall timer_getoverrun(2). -func TimerGetoverrun(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func TimerGetoverrun(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { timerID := linux.TimerID(args[0].Value) o, err := t.IntervalTimerGetoverrun(timerID) @@ -178,7 +178,7 @@ func TimerGetoverrun(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kern } // TimerDelete implements linux syscall timer_delete(2). -func TimerDelete(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func TimerDelete(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { timerID := linux.TimerID(args[0].Value) return 0, nil, t.IntervalTimerDelete(timerID) } diff --git a/pkg/sentry/syscalls/linux/sys_timerfd.go b/pkg/sentry/syscalls/linux/sys_timerfd.go index 784cec917..80fe97c98 100644 --- a/pkg/sentry/syscalls/linux/sys_timerfd.go +++ b/pkg/sentry/syscalls/linux/sys_timerfd.go @@ -24,7 +24,7 @@ import ( ) // TimerfdCreate implements Linux syscall timerfd_create(2). -func TimerfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func TimerfdCreate(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { clockID := args[0].Int() flags := args[1].Int() @@ -65,7 +65,7 @@ func TimerfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel } // TimerfdSettime implements Linux syscall timerfd_settime(2). -func TimerfdSettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func TimerfdSettime(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() flags := args[1].Int() newValAddr := args[2].Pointer() @@ -105,7 +105,7 @@ func TimerfdSettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne } // TimerfdGettime implements Linux syscall timerfd_gettime(2). -func TimerfdGettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func TimerfdGettime(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() curValAddr := args[1].Pointer() diff --git a/pkg/sentry/syscalls/linux/sys_tls_amd64.go b/pkg/sentry/syscalls/linux/sys_tls_amd64.go index bde672d67..655cdc6fe 100644 --- a/pkg/sentry/syscalls/linux/sys_tls_amd64.go +++ b/pkg/sentry/syscalls/linux/sys_tls_amd64.go @@ -27,7 +27,7 @@ import ( // ArchPrctl implements linux syscall arch_prctl(2). // It sets architecture-specific process or thread state for t. -func ArchPrctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ArchPrctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { switch args[0].Int() { case linux.ARCH_GET_FS: addr := args[1].Pointer() @@ -46,7 +46,7 @@ func ArchPrctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys return 0, nil, linuxerr.EPERM } case linux.ARCH_GET_GS, linux.ARCH_SET_GS: - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) fallthrough default: return 0, nil, linuxerr.EINVAL diff --git a/pkg/sentry/syscalls/linux/sys_tls_arm64.go b/pkg/sentry/syscalls/linux/sys_tls_arm64.go index dfa684387..93dcb64b4 100644 --- a/pkg/sentry/syscalls/linux/sys_tls_arm64.go +++ b/pkg/sentry/syscalls/linux/sys_tls_arm64.go @@ -24,6 +24,6 @@ import ( ) // ArchPrctl is not defined for ARM64. -func ArchPrctl(*kernel.Task, arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ArchPrctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, linuxerr.ENOSYS } diff --git a/pkg/sentry/syscalls/linux/sys_utsname.go b/pkg/sentry/syscalls/linux/sys_utsname.go index 4e945d2c0..b70137b33 100644 --- a/pkg/sentry/syscalls/linux/sys_utsname.go +++ b/pkg/sentry/syscalls/linux/sys_utsname.go @@ -22,7 +22,7 @@ import ( ) // Uname implements linux syscall uname. -func Uname(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Uname(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { version := t.SyscallTable().Version uts := t.UTSNamespace() @@ -51,7 +51,7 @@ func Uname(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall } // Setdomainname implements Linux syscall setdomainname. -func Setdomainname(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Setdomainname(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { nameAddr := args[0].Pointer() size := args[1].Int() @@ -73,7 +73,7 @@ func Setdomainname(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel } // Sethostname implements Linux syscall sethostname. -func Sethostname(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Sethostname(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { nameAddr := args[0].Pointer() size := args[1].Int() diff --git a/pkg/sentry/syscalls/linux/sys_xattr.go b/pkg/sentry/syscalls/linux/sys_xattr.go index 592eece48..1f86a610c 100644 --- a/pkg/sentry/syscalls/linux/sys_xattr.go +++ b/pkg/sentry/syscalls/linux/sys_xattr.go @@ -27,12 +27,12 @@ import ( ) // ListXattr implements Linux syscall listxattr(2). -func ListXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func ListXattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return listxattr(t, args, followFinalSymlink) } // Llistxattr implements Linux syscall llistxattr(2). -func Llistxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Llistxattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return listxattr(t, args, nofollowFinalSymlink) } @@ -63,7 +63,7 @@ func listxattr(t *kernel.Task, args arch.SyscallArguments, shouldFollowFinalSyml } // Flistxattr implements Linux syscall flistxattr(2). -func Flistxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Flistxattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() listAddr := args[1].Pointer() size := args[2].SizeT() @@ -86,12 +86,12 @@ func Flistxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy } // GetXattr implements Linux syscall getxattr(2). -func GetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func GetXattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return getxattr(t, args, followFinalSymlink) } // Lgetxattr implements Linux syscall lgetxattr(2). -func Lgetxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Lgetxattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return getxattr(t, args, nofollowFinalSymlink) } @@ -131,7 +131,7 @@ func getxattr(t *kernel.Task, args arch.SyscallArguments, shouldFollowFinalSymli } // Fgetxattr implements Linux syscall fgetxattr(2). -func Fgetxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fgetxattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() nameAddr := args[1].Pointer() valueAddr := args[2].Pointer() @@ -160,12 +160,12 @@ func Fgetxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // SetXattr implements Linux syscall setxattr(2). -func SetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func SetXattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, setxattr(t, args, followFinalSymlink) } // Lsetxattr implements Linux syscall lsetxattr(2). -func Lsetxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Lsetxattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, setxattr(t, args, nofollowFinalSymlink) } @@ -207,7 +207,7 @@ func setxattr(t *kernel.Task, args arch.SyscallArguments, shouldFollowFinalSymli } // Fsetxattr implements Linux syscall fsetxattr(2). -func Fsetxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fsetxattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() nameAddr := args[1].Pointer() valueAddr := args[2].Pointer() @@ -241,12 +241,12 @@ func Fsetxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys } // RemoveXattr implements Linux syscall removexattr(2). -func RemoveXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func RemoveXattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, removexattr(t, args, followFinalSymlink) } // Lremovexattr implements Linux syscall lremovexattr(2). -func Lremovexattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Lremovexattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, removexattr(t, args, nofollowFinalSymlink) } @@ -273,7 +273,7 @@ func removexattr(t *kernel.Task, args arch.SyscallArguments, shouldFollowFinalSy } // Fremovexattr implements Linux syscall fremovexattr(2). -func Fremovexattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { +func Fremovexattr(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() nameAddr := args[1].Pointer() diff --git a/pkg/sentry/syscalls/syscalls.go b/pkg/sentry/syscalls/syscalls.go index cd60f7eed..7d5c9d8b3 100644 --- a/pkg/sentry/syscalls/syscalls.go +++ b/pkg/sentry/syscalls/syscalls.go @@ -77,7 +77,7 @@ func Error(name string, err error, note string, urls []string) kernel.Syscall { } return kernel.Syscall{ Name: name, - Fn: func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + Fn: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { return 0, nil, err }, SupportLevel: kernel.SupportUnimplemented, @@ -94,8 +94,8 @@ func ErrorWithEvent(name string, err error, note string, urls []string) kernel.S } return kernel.Syscall{ Name: name, - Fn: func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { - t.Kernel().EmitUnimplementedEvent(t) + Fn: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, nil, err }, SupportLevel: kernel.SupportUnimplemented, @@ -113,11 +113,11 @@ func CapError(name string, c linux.Capability, note string, urls []string) kerne } return kernel.Syscall{ Name: name, - Fn: func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + Fn: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { if !t.HasCapability(c) { return 0, nil, linuxerr.EPERM } - t.Kernel().EmitUnimplementedEvent(t) + t.Kernel().EmitUnimplementedEvent(t, sysno) return 0, nil, linuxerr.ENOSYS }, SupportLevel: kernel.SupportUnimplemented, From d6ed799adebe831cdc3efcaf08693a3cf525b808 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 22 Mar 2023 12:23:24 -0700 Subject: [PATCH 36/49] systrap: save context pointer on sysmsg We don't need to calculate an address from context_id each time. PiperOrigin-RevId: 518640998 --- pkg/sentry/platform/systrap/stub_unsafe.go | 2 ++ pkg/sentry/platform/systrap/subprocess.go | 5 ++--- pkg/sentry/platform/systrap/sysmsg/build.bzl | 1 + .../systrap/sysmsg/sighandler_amd64.c | 6 ++--- .../systrap/sysmsg/sighandler_arm64.c | 5 +++-- .../systrap/sysmsg/syshandler_amd64.S | 13 ++--------- pkg/sentry/platform/systrap/sysmsg/sysmsg.go | 7 ++---- pkg/sentry/platform/systrap/sysmsg/sysmsg.h | 15 +++++-------- .../platform/systrap/sysmsg/sysmsg_lib.c | 22 +++++++++++-------- .../platform/systrap/sysmsg/sysmsg_offsets.h | 3 +-- 10 files changed, 33 insertions(+), 46 deletions(-) diff --git a/pkg/sentry/platform/systrap/stub_unsafe.go b/pkg/sentry/platform/systrap/stub_unsafe.go index 7db687dc6..51e389ce4 100644 --- a/pkg/sentry/platform/systrap/stub_unsafe.go +++ b/pkg/sentry/platform/systrap/stub_unsafe.go @@ -222,6 +222,8 @@ func stubInit() { *p = deepSleepTimeout p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_handshake_timeout))) *p = handshakeTimeout + p = (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_region))) + *p = uint64(stubContextRegion) archState := (*sysmsg.ArchState)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_arch_state))) archState.Init() exp := (*uint64)(unsafe.Pointer(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_context_decoupling_exp))) diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index ed2f71e86..7932187fe 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -1039,14 +1039,13 @@ func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *a sysThread.setMsg(sysmsg.StackAddrToMsg(sentryStackAddr)) sysThread.msg.Init(threadID) if contextDecouplingExp { - sysThread.msg.ContextID = invalidContextID + sysThread.msg.Context = 0 } else { c.sharedContext.setThreadID(threadID) - sysThread.msg.ContextID = c.sharedContext.contextID + sysThread.msg.Context = uint64(stubContextRegion + uintptr(c.sharedContext.contextID)*sysmsg.AllocatedSizeofThreadContextStruct) } sysThread.msg.Self = uint64(sysmsgStackAddr + sysmsg.MsgOffsetFromSharedStack) sysThread.msg.SyshandlerStack = uint64(sysmsg.StackAddrToSyshandlerStack(sysThread.sysmsgPerThreadMemAddr())) - sysThread.msg.ContextRegion = uint64(stubContextRegion) sysThread.msg.Syshandler = uint64(stubSysmsgStart + uintptr(sysmsg.Sighandler_blob_offset____export_syshandler)) sysThread.msg.State.Set(sysmsg.ThreadStateInitializing) diff --git a/pkg/sentry/platform/systrap/sysmsg/build.bzl b/pkg/sentry/platform/systrap/sysmsg/build.bzl index 52fbd4e99..277dbf8b8 100644 --- a/pkg/sentry/platform/systrap/sysmsg/build.bzl +++ b/pkg/sentry/platform/systrap/sysmsg/build.bzl @@ -9,6 +9,7 @@ def cc_pie_obj(name, srcs, outs): srcs = srcs, outs = outs, cmd = "$(CC) $(CC_FLAGS) " + + "-Wall -Werror -Wno-unused-command-line-argument " + "-fpie " + # -01 is required for clang to avoid making use of memcpy when # building for ARM64. For some reason when no optimization is turned diff --git a/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c b/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c index 193fe86f3..0d9ee1d78 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c +++ b/pkg/sentry/platform/systrap/sysmsg/sighandler_amd64.c @@ -205,7 +205,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { return; } - struct thread_context *ctx = thread_context_addr(sysmsg); + struct thread_context *ctx = sysmsg->context; if (signo == SIGCHLD) { // If the current thread is in syshandler, an interrupt has to be postponed, @@ -254,8 +254,6 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { switch (signo) { case SIGSYS: { - int si_sysno = siginfo->si_syscall; - int i; ctx_state = CONTEXT_STATE_SYSCALL; // Check whether this syscall can be replaced on a function call or not. @@ -354,7 +352,7 @@ void __syshandler() { int state = __atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE); if (state != THREAD_STATE_PREP) panic(state); - struct thread_context *ctx = thread_context_addr(sysmsg); + struct thread_context *ctx = sysmsg->context; enum context_state ctx_state = CONTEXT_STATE_SYSCALL_TRAP; ctx->signo = SIGSYS; diff --git a/pkg/sentry/platform/systrap/sysmsg/sighandler_arm64.c b/pkg/sentry/platform/systrap/sysmsg/sighandler_arm64.c index ed3c0b036..18fb594bb 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sighandler_arm64.c +++ b/pkg/sentry/platform/systrap/sysmsg/sighandler_arm64.c @@ -104,7 +104,7 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { return; } - struct thread_context *ctx = thread_context_addr(sysmsg); + struct thread_context *ctx = sysmsg->context; uint32_t ctx_state = CONTEXT_STATE_INVALID; ctx->signo = signo; @@ -181,7 +181,8 @@ void __export_sighandler(int signo, siginfo_t *siginfo, void *_ucontext) { void restore_state(struct sysmsg *sysmsg, struct thread_context *ctx, void *_ucontext) { ucontext_t *ucontext = _ucontext; - struct fpsimd_context *fpctx = &ucontext->uc_mcontext.__reserved; + struct fpsimd_context *fpctx = + (struct fpsimd_context *)&ucontext->uc_mcontext.__reserved; uint8_t *fpStatePointer = (uint8_t *)&fpctx->fpsr; if (__export_context_decoupling_exp && diff --git a/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S b/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S index d5891e5cb..d7ba988c7 100644 --- a/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S +++ b/pkg/sentry/platform/systrap/sysmsg/syshandler_amd64.S @@ -18,15 +18,6 @@ // Helper macros: //////////////////////////////////////// -// load_thread_context loads the address of the thread context slot for the current -// context. -// Clobbers %rflags; loads address into %rcx. -.macro load_thread_context_addr - movl %gs:offsetof_sysmsg_context_id, %ecx - shl $THREAD_CONTEXT_STRUCT_BITSHIFT, %rcx - add %gs:offsetof_sysmsg_context_region, %rcx -.endm - // prepare_enter_syshandler does the following: // - saves all registers that are restorable onto the thread_context struct. // - loads the address of the thread_context struct into %rcx. @@ -37,7 +28,7 @@ // load_thread_context_addr overwrites %rcx. push %rcx - load_thread_context_addr + movq %gs:offsetof_sysmsg_context, %rcx // Registers listed in order as written in ptregs: movq %r15, offsetof_thread_context_ptregs_r15(%rcx) @@ -200,7 +191,7 @@ __export_syshandler: .type asm_restore_state, @function; asm_restore_state: // thread_context may have changed, therefore we reload it into %rcx anew. - load_thread_context_addr + movq %gs:offsetof_sysmsg_context, %rcx restore_fpstate prepare_exit_syshandler diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go index a103454b5..7f06f24be 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go @@ -152,13 +152,10 @@ type Msg struct { // State indicates to the sentry what the sysmsg thread is doing at a given // moment. State ThreadState - // ContextRegion defines the ThreadContext memory region start within - // the sysmsg thread address space. - ContextRegion uint64 // ContextID is the ID of the ThreadContext struct that the current // sysmsg thread is is processing. This ID is used in the {sig|sys}handler // to find the offset to the correct ThreadContext struct location. - ContextID uint32 + Context uint64 // FaultJump is the size of a faulted instruction. FaultJump int32 @@ -352,7 +349,7 @@ func (m *Msg) String() string { fmt.Fprintf(&b, "sysmsg.Msg{msg: %x state %d", m.Self, m.State) fmt.Fprintf(&b, " err %x line %d debug %x", m.Err, m.Line, m.Debug) fmt.Fprintf(&b, " app stack %x", m.AppStack) - fmt.Fprintf(&b, " contextID %d", m.ContextID) + fmt.Fprintf(&b, " context %x", m.Context) b.WriteString("}") return b.String() diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h index b1def2359..8c00557b3 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h @@ -16,6 +16,7 @@ #define THIRD_PARTY_GVISOR_PKG_SENTRY_PLATFORM_SYSTRAP_SYSMSG_SYSMSG_H_ #include +#include #include #include "sysmsg_offsets.h" // NOLINT @@ -48,6 +49,8 @@ enum thread_state { THREAD_STATE_INITIALIZING, }; +struct thread_context; + // sysmsg contains the current state of the sysmsg thread. See: sysmsg.go:Msg struct sysmsg { struct sysmsg *self; @@ -57,8 +60,7 @@ struct sysmsg { uint64_t app_stack; uint32_t interrupt; uint32_t state; - uint64_t context_region; - uint32_t context_id; + struct thread_context *context; // The fields above have offsets defined in sysmsg_offsets*.h @@ -142,13 +144,6 @@ static struct sysmsg *sysmsg_addr(void *sp) { return (struct sysmsg *)(sp + MSG_OFFSET_FROM_START); } -static struct thread_context *thread_context_addr(struct sysmsg *sysmsg) { - uint64_t tcid = __atomic_load_n(&sysmsg->context_id, __ATOMIC_ACQUIRE); - return (struct thread_context *)(sysmsg->context_region + - tcid * - ALLOCATED_SIZEOF_THREAD_CONTEXT_STRUCT); -} - long __syscall(long n, long a1, long a2, long a3, long a4, long a5, long a6); struct __kernel_timespec; @@ -158,7 +153,7 @@ long sys_futex(uint32_t *addr, int op, int val, struct __kernel_timespec *tv, static void __panic(int err, long line) { void *sp = sysmsg_sp(); struct sysmsg *sysmsg = sysmsg_addr(sp); - struct thread_context *ctx = thread_context_addr(sysmsg); + struct thread_context *ctx = sysmsg->context; sysmsg->err = err; sysmsg->err_line = line; // Normally sentry waits on sysmsg->state. diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c index ec5328d60..d8b707ea2 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c @@ -79,6 +79,14 @@ static __inline__ unsigned long rdtsc(void) { static __inline__ void spinloop(void) { asm volatile("yield" : : : "memory"); } #endif +void *__export_context_region; + +static struct thread_context *thread_context_addr(uint32_t tcid) { + return (struct thread_context *)(__export_context_region + + tcid * + ALLOCATED_SIZEOF_THREAD_CONTEXT_STRUCT); +} + void memcpy(uint8_t *dest, uint8_t *src, size_t n) { for (size_t i = 0; i < n; i += 1) { dest[i] = src[i]; @@ -180,8 +188,8 @@ struct thread_context *queue_get_context(struct sysmsg *sysmsg) { if (context_id > MAX_STUB_THREADS) { panic(context_id); } - sysmsg->context_id = context_id; - struct thread_context *ctx = thread_context_addr(sysmsg); + struct thread_context *ctx = thread_context_addr(context_id); + sysmsg->context = ctx; __atomic_store_n(&ctx->acked, 1, __ATOMIC_RELEASE); __atomic_store_n(&ctx->thread_id, sysmsg->thread_id, __ATOMIC_RELEASE); return ctx; @@ -235,12 +243,11 @@ struct thread_context *switch_context(struct sysmsg *sysmsg, } } - uint32_t old_ctx_id = sysmsg->context_id; + struct thread_context *old_ctx = sysmsg->context; ctx = get_context(sysmsg); - if (old_ctx_id != sysmsg->context_id || - ctx->last_thread_id != sysmsg->thread_id) { + if (old_ctx != ctx || ctx->last_thread_id != sysmsg->thread_id) { ctx->fpstate_changed = 1; } @@ -332,10 +339,7 @@ void verify_offsets() { BUILD_BUG_ON(offsetof_sysmsg_app_stack != offsetof(struct sysmsg, app_stack)); BUILD_BUG_ON(offsetof_sysmsg_interrupt != offsetof(struct sysmsg, interrupt)); BUILD_BUG_ON(offsetof_sysmsg_state != offsetof(struct sysmsg, state)); - BUILD_BUG_ON(offsetof_sysmsg_context_id != - offsetof(struct sysmsg, context_id)); - BUILD_BUG_ON(offsetof_sysmsg_context_region != - offsetof(struct sysmsg, context_region)); + BUILD_BUG_ON(offsetof_sysmsg_context != offsetof(struct sysmsg, context)); BUILD_BUG_ON(offsetof_thread_context_fpstate != offsetof(struct thread_context, fpstate)); diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h index fe02d4a68..5daed1bbd 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h @@ -39,8 +39,7 @@ #define offsetof_sysmsg_app_stack 0x20 #define offsetof_sysmsg_interrupt 0x28 #define offsetof_sysmsg_state 0x2c -#define offsetof_sysmsg_context_region 0x30 -#define offsetof_sysmsg_context_id 0x38 +#define offsetof_sysmsg_context 0x30 #define offsetof_thread_context_fpstate 0x0 #define offsetof_thread_context_fpstate_changed MAX_FPSTATE_LEN From 69fae5353a6b71264afb3069d549e1bbdd88d643 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Wed, 22 Mar 2023 15:09:38 -0700 Subject: [PATCH 37/49] gro: remove unnecessary restriction on GRO-able packets DF doesn't need to be set. Linux also allows GRO on packets without DF set. This fixes an issue where loopback traffic couldn't be GRO'd because gVisor never sets the DF bit (we don't implement PMTUD yet). PiperOrigin-RevId: 518684608 --- pkg/tcpip/stack/gro.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/tcpip/stack/gro.go b/pkg/tcpip/stack/gro.go index 29a1b7061..acbb8f297 100644 --- a/pkg/tcpip/stack/gro.go +++ b/pkg/tcpip/stack/gro.go @@ -451,9 +451,9 @@ func (gd *groDispatcher) dispatch4(pkt PacketBufferPtr, ep NetworkEndpoint) { } ipHdr := header.IPv4(hdrBytes) - // We only handle atomic packets. That's the vast majority of traffic, - // and simplifies handling. - if ipHdr.FragmentOffset() != 0 || ipHdr.Flags()&header.IPv4FlagMoreFragments != 0 || ipHdr.Flags()&header.IPv4FlagDontFragment == 0 { + // We don't handle fragments. That should be the vast majority of + // traffic, and simplifies handling. + if ipHdr.FragmentOffset() != 0 || ipHdr.Flags()&header.IPv4FlagMoreFragments != 0 { ep.HandlePacket(pkt) return } From fa7aa5b4e2e5d958dff4f8acb3b783c96878efe0 Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Thu, 23 Mar 2023 00:06:44 -0700 Subject: [PATCH 38/49] Decouple file_handle_sharing and cache settings in the gofer client. Earlier, both these settings were controlled by the cache= option. Only cache_none was able to disable file handle sharing. But there can be cases where we want the FS cache but disable file handle sharing. Instead of adding yet another cache enum, decouple these settings so they can be set independently. As a result, we don't need cache_none anymore, get rid of it. PiperOrigin-RevId: 518780704 --- pkg/sentry/fsimpl/gofer/filesystem.go | 9 ++++----- pkg/sentry/fsimpl/gofer/gofer.go | 29 ++++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pkg/sentry/fsimpl/gofer/filesystem.go b/pkg/sentry/fsimpl/gofer/filesystem.go index 97cbdde45..fc9dec077 100644 --- a/pkg/sentry/fsimpl/gofer/filesystem.go +++ b/pkg/sentry/fsimpl/gofer/filesystem.go @@ -1736,11 +1736,10 @@ func (fs *filesystem) MountOptions() string { case InteropModeWritethrough: optsKV = append(optsKV, mopt{moptCache, cacheFSCacheWritethrough}) case InteropModeShared: - if fs.opts.regularFilesUseSpecialFileFD { - optsKV = append(optsKV, mopt{moptCache, cacheNone}) - } else { - optsKV = append(optsKV, mopt{moptCache, cacheRemoteRevalidating}) - } + optsKV = append(optsKV, mopt{moptCache, cacheRemoteRevalidating}) + } + if fs.opts.regularFilesUseSpecialFileFD { + optsKV = append(optsKV, mopt{moptDisableFileHandleSharing, nil}) } if fs.opts.forcePageCache { optsKV = append(optsKV, mopt{moptForcePageCache, nil}) diff --git a/pkg/sentry/fsimpl/gofer/gofer.go b/pkg/sentry/fsimpl/gofer/gofer.go index 73825ddbb..ea807e86c 100644 --- a/pkg/sentry/fsimpl/gofer/gofer.go +++ b/pkg/sentry/fsimpl/gofer/gofer.go @@ -73,16 +73,17 @@ const Name = "9p" // Mount option names for goferfs. const ( - moptTransport = "trans" - moptReadFD = "rfdno" - moptWriteFD = "wfdno" - moptAname = "aname" - moptDfltUID = "dfltuid" - moptDfltGID = "dfltgid" - moptCache = "cache" - moptForcePageCache = "force_page_cache" - moptLimitHostFDTranslation = "limit_host_fd_translation" - moptOverlayfsStaleRead = "overlayfs_stale_read" + moptTransport = "trans" + moptReadFD = "rfdno" + moptWriteFD = "wfdno" + moptAname = "aname" + moptDfltUID = "dfltuid" + moptDfltGID = "dfltgid" + moptCache = "cache" + moptForcePageCache = "force_page_cache" + moptLimitHostFDTranslation = "limit_host_fd_translation" + moptOverlayfsStaleRead = "overlayfs_stale_read" + moptDisableFileHandleSharing = "disable_file_handle_sharing" // Directfs options. moptDirectfs = "directfs" @@ -90,7 +91,6 @@ const ( // Valid values for the "cache" mount option. const ( - cacheNone = "none" cacheFSCache = "fscache" cacheFSCacheWritethrough = "fscache_writethrough" cacheRemoteRevalidating = "remote_revalidating" @@ -422,9 +422,6 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt fsopts.interop = InteropModeExclusive case cacheFSCacheWritethrough: fsopts.interop = InteropModeWritethrough - case cacheNone: - fsopts.regularFilesUseSpecialFileFD = true - fallthrough case cacheRemoteRevalidating: fsopts.interop = InteropModeShared default: @@ -459,6 +456,10 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt } // Handle simple flags. + if _, ok := mopts[moptDisableFileHandleSharing]; ok { + delete(mopts, moptDisableFileHandleSharing) + fsopts.regularFilesUseSpecialFileFD = true + } if _, ok := mopts[moptForcePageCache]; ok { delete(mopts, moptForcePageCache) fsopts.forcePageCache = true From f727f06c81a1ec240ad7f7ab865a031488ff4ab2 Mon Sep 17 00:00:00 2001 From: Konstantin Bogomolov Date: Thu, 23 Mar 2023 11:38:17 -0700 Subject: [PATCH 39/49] Add debug logging to systrap futex waits. In general it is probably a good idea to set a timeout on any futex waits that the sentry is doing. For now just output some helpful logs about what the shared memory looks like; in the future we may want to do something more useful on ETIMEDOUT events. PiperOrigin-RevId: 518919966 --- pkg/sentry/platform/systrap/shared_context.go | 4 +- pkg/sentry/platform/systrap/subprocess.go | 4 +- pkg/sentry/platform/systrap/sysmsg/BUILD | 2 + pkg/sentry/platform/systrap/sysmsg/sysmsg.go | 9 ++- .../platform/systrap/sysmsg/sysmsg_unsafe.go | 62 ++++++++++++++++--- pkg/sentry/platform/systrap/sysmsg_thread.go | 5 +- .../platform/systrap/sysmsg_thread_unsafe.go | 9 ++- 7 files changed, 75 insertions(+), 20 deletions(-) diff --git a/pkg/sentry/platform/systrap/shared_context.go b/pkg/sentry/platform/systrap/shared_context.go index a3b295d0e..d867ab3d2 100644 --- a/pkg/sentry/platform/systrap/shared_context.go +++ b/pkg/sentry/platform/systrap/shared_context.go @@ -163,5 +163,7 @@ func (sc *sharedContext) resetAcked() { } func (sc *sharedContext) sleepOnState(state sysmsg.ContextState) { - sc.shared.SleepOnState(state) + if errno := sc.shared.SleepOnState(state, sc); errno != 0 { + panic(fmt.Sprintf("error waiting for state: %v", errno)) + } } diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index 7932187fe..e819ab333 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -743,7 +743,7 @@ func (s *subprocess) switchToApp(c *context, ac *arch.Context64) (isSyscall bool restoreFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) msg.EnableSentryFastPath() - sysThread.waitEvent(sysmsg.ThreadStateDone) + sysThread.waitEvent(sysmsg.ThreadStateDone, ctx) // Check if there's been an error. if msg.Err != 0 { @@ -1084,7 +1084,7 @@ func (s *subprocess) createSysmsgThread(tregs *arch.Registers, c *context, ac *a } if !contextDecouplingExp { - sysThread.waitEvent(sysmsg.ThreadStateNone) + sysThread.waitEvent(sysmsg.ThreadStateNone, c.sharedContext) if msg := sysThread.msg; msg.Err != 0 { panic(fmt.Sprintf("stub thread failed: %v (line %v)", msg.Err, msg.Line)) } diff --git a/pkg/sentry/platform/systrap/sysmsg/BUILD b/pkg/sentry/platform/systrap/sysmsg/BUILD index ba50cccea..9b4d15466 100644 --- a/pkg/sentry/platform/systrap/sysmsg/BUILD +++ b/pkg/sentry/platform/systrap/sysmsg/BUILD @@ -131,6 +131,8 @@ go_library( "//pkg/cpuid", "//pkg/errors", "//pkg/hostarch", + "//pkg/log", + "//pkg/sentry/platform/interrupt", "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go index 7f06f24be..521654d00 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go @@ -152,9 +152,8 @@ type Msg struct { // State indicates to the sentry what the sysmsg thread is doing at a given // moment. State ThreadState - // ContextID is the ID of the ThreadContext struct that the current - // sysmsg thread is is processing. This ID is used in the {sig|sys}handler - // to find the offset to the correct ThreadContext struct location. + // Context is a pointer to the ThreadContext struct that the current sysmsg + // thread is processing. Context uint64 // FaultJump is the size of a faulted instruction. @@ -350,6 +349,7 @@ func (m *Msg) String() string { fmt.Fprintf(&b, " err %x line %d debug %x", m.Err, m.Line, m.Debug) fmt.Fprintf(&b, " app stack %x", m.AppStack) fmt.Fprintf(&b, " context %x", m.Context) + fmt.Fprintf(&b, " ThreadID %d", m.ThreadID) b.WriteString("}") return b.String() @@ -361,6 +361,9 @@ func (c *ThreadContext) String() string { fmt.Fprintf(&b, " fault addr %x syscall %d", c.SignalInfo.Addr(), c.SignalInfo.Syscall()) fmt.Fprintf(&b, " ip %x sp %x", c.Regs.InstructionPointer(), c.Regs.StackPointer()) fmt.Fprintf(&b, " FPStateChanged %d Regs %+v", c.FPStateChanged, c.Regs) + fmt.Fprintf(&b, " Interrupt %d", c.Interrupt) + fmt.Fprintf(&b, " ThreadID %d LastThreadID %d", c.ThreadID, c.LastThreadID) + fmt.Fprintf(&b, " SentryFastPath %d Acked %d", c.SentryFastPath, c.Acked) fmt.Fprintf(&b, " signo: %d, siginfo: %+v", c.Signo, c.SignalInfo) fmt.Fprintf(&b, " debug %d", atomic.LoadUint64(&c.Debug)) b.WriteString("}") diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go index 3f762e116..7ff0eab95 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go @@ -15,21 +15,69 @@ package sysmsg import ( - "fmt" "syscall" "unsafe" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/log" + "gvisor.dev/gvisor/pkg/sentry/platform/interrupt" ) -// SleepOnState makes the caller sleep on the ThreadContext.State futex. -func (c *ThreadContext) SleepOnState(curState ContextState) { - _, _, errno := unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&c.State)), - linux.FUTEX_WAIT, uintptr(curState), 0, 0, 0) - if errno != 0 && errno != unix.EAGAIN && errno != unix.EINTR { - panic(fmt.Sprintf("error waiting for state: %v", errno)) +const maxFutexSleepSeconds = 60 + +// SleepOnState makes the caller sleep on the Msg.State futex. +func (m *Msg) SleepOnState(curState ThreadState, interruptor interrupt.Receiver) syscall.Errno { + futexTimeout := unix.Timespec{ + Sec: maxFutexSleepSeconds, + Nsec: 0, } + sentInterruptOnce := false + errno := syscall.Errno(0) + for { + _, _, errno = unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&m.State)), + linux.FUTEX_WAIT, uintptr(curState), uintptr(unsafe.Pointer(&futexTimeout)), 0, 0) + if errno == unix.ETIMEDOUT { + interruptor.NotifyInterrupt() + if !sentInterruptOnce { + log.Warningf("Systrap task goroutine has been waiting on Msg.State futex too long. Msg: %s", m.String()) + } + sentInterruptOnce = true + } else { + break + } + } + if errno == unix.EAGAIN || errno == unix.EINTR { + errno = 0 + } + return errno +} + +// SleepOnState makes the caller sleep on the ThreadContext.State futex. +func (c *ThreadContext) SleepOnState(curState ContextState, interruptor interrupt.Receiver) syscall.Errno { + futexTimeout := unix.Timespec{ + Sec: maxFutexSleepSeconds, + Nsec: 0, + } + sentInterruptOnce := false + errno := syscall.Errno(0) + for { + _, _, errno = unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&c.State)), + linux.FUTEX_WAIT, uintptr(curState), uintptr(unsafe.Pointer(&futexTimeout)), 0, 0) + if errno == unix.ETIMEDOUT { + interruptor.NotifyInterrupt() + if !sentInterruptOnce { + log.Warningf("Systrap task goroutine has been waiting on ThreadContext.State futex too long. ThreadContext: %s", c.String()) + } + sentInterruptOnce = true + } else { + break + } + } + if errno == unix.EAGAIN || errno == unix.EINTR { + errno = 0 + } + return errno } // WakeSysmsgThread calls futex wake on Sysmsg.State. diff --git a/pkg/sentry/platform/systrap/sysmsg_thread.go b/pkg/sentry/platform/systrap/sysmsg_thread.go index ddcd105e5..b6b5c29c0 100644 --- a/pkg/sentry/platform/systrap/sysmsg_thread.go +++ b/pkg/sentry/platform/systrap/sysmsg_thread.go @@ -24,6 +24,7 @@ import ( "gvisor.dev/gvisor/pkg/seccomp" "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/memmap" + "gvisor.dev/gvisor/pkg/sentry/platform/interrupt" "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" ) @@ -116,7 +117,7 @@ func (p *sysmsgThread) mapPrivateStack(addr uintptr, size uintptr) error { return err } -func (p *sysmsgThread) waitEvent(switchToState sysmsg.ThreadState) { +func (p *sysmsgThread) waitEvent(switchToState sysmsg.ThreadState, interruptor interrupt.Receiver) { msg := p.msg wakeup := false acked := atomic.LoadUint32(&msg.AckedEvents) @@ -127,7 +128,7 @@ func (p *sysmsgThread) waitEvent(switchToState sysmsg.ThreadState) { acked-- } - if errno := futexWaitForState(msg, sysmsg.ThreadStateEvent, wakeup, acked); errno != 0 { + if errno := futexWaitForState(msg, sysmsg.ThreadStateEvent, wakeup, acked, interruptor); errno != 0 { panic(fmt.Sprintf("error waiting for state: %v", errno)) } } diff --git a/pkg/sentry/platform/systrap/sysmsg_thread_unsafe.go b/pkg/sentry/platform/systrap/sysmsg_thread_unsafe.go index 9c8cf1df3..90c016419 100644 --- a/pkg/sentry/platform/systrap/sysmsg_thread_unsafe.go +++ b/pkg/sentry/platform/systrap/sysmsg_thread_unsafe.go @@ -24,6 +24,7 @@ import ( "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/sentry/arch" + "gvisor.dev/gvisor/pkg/sentry/platform/interrupt" "gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg" ) @@ -101,7 +102,7 @@ func exitsyscall() const deepSleepTimeout = uint64(80000) const handshakeTimeout = uint64(1000) -func futexWaitForState(msg *sysmsg.Msg, state sysmsg.ThreadState, wakeup bool, acked uint32) syscall.Errno { +func futexWaitForState(msg *sysmsg.Msg, state sysmsg.ThreadState, wakeup bool, acked uint32, interruptor interrupt.Receiver) syscall.Errno { slowPath := false errno := syscall.Errno(0) start := cputicks() @@ -141,12 +142,10 @@ func futexWaitForState(msg *sysmsg.Msg, state sysmsg.ThreadState, wakeup bool, a } if slowPath { - _, _, errno = unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&msg.State)), - linux.FUTEX_WAIT, uintptr(curState), 0, 0, 0) - if errno != 0 && errno != unix.EAGAIN && errno != unix.EINTR { + errno = msg.SleepOnState(curState, interruptor) + if errno != 0 { break } - errno = 0 } else { spinloop() } From 7b5cd4dda5bea482ea5b2ddbc5ab22ae993299c0 Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Thu, 23 Mar 2023 11:49:19 -0700 Subject: [PATCH 40/49] `runsc`: Prohibit runsc metrics from starting with the prefix "`meta_`". This prefix is used by the metric server to synthesize its own metrics. If the sandbox were to define metrics with the same name, they would conflict. By having this prefix check, this prevents a malicious sandbox from defining metrics that conflict with those that the metric server is trying to export. PiperOrigin-RevId: 518922970 --- pkg/prometheus/prometheus_test.go | 22 ++++++++++++++++++++++ pkg/prometheus/prometheus_verify.go | 8 ++++++++ runsc/cmd/metric_server.go | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/pkg/prometheus/prometheus_test.go b/pkg/prometheus/prometheus_test.go index c455a4e7b..49e9bc0cc 100644 --- a/pkg/prometheus/prometheus_test.go +++ b/pkg/prometheus/prometheus_test.go @@ -364,6 +364,28 @@ func TestVerifier(t *testing.T) { ), WantVerifierCreationErr: true, }, + { + Name: "Prometheus metric name starts with reserved prefix", + Registration: newMetricRegistration(&metricMetadata{ + PB: &pb.MetricMetadata{ + Name: "metaFooBar", + PrometheusName: "meta_foo_bar", + Type: pb.MetricMetadata_TYPE_UINT64, + }}, + ), + WantVerifierCreationErr: true, + }, + { + Name: "Prometheus metric name does not starts with reserved prefix but non-Prometheus metric name does", + Registration: newMetricRegistration(&metricMetadata{ + PB: &pb.MetricMetadata{ + Name: "metaFooBar", + PrometheusName: "not_meta_foo_bar", + Type: pb.MetricMetadata_TYPE_UINT64, + }}, + ), + WantVerifierCreationErr: false, + }, { Name: "no buckets", Registration: newMetricRegistration(&metricMetadata{ diff --git a/pkg/prometheus/prometheus_verify.go b/pkg/prometheus/prometheus_verify.go index 5cf85296f..78204487f 100644 --- a/pkg/prometheus/prometheus_verify.go +++ b/pkg/prometheus/prometheus_verify.go @@ -30,6 +30,11 @@ const ( // maxExportStaleness is the maximum allowed age of a snapshot when it is verified. // Used to avoid exporting snapshots from bogus times from ages past. maxExportStaleness = 10 * time.Second + + // MetaMetricPrefix is a prefix used for metrics defined by the metric server, + // as opposed to metrics generated by each sandbox. + // For this reason, this prefix is not allowed to be used in sandbox metrics. + MetaMetricPrefix = "meta_" ) // internedStringMap allows for interning strings. @@ -260,6 +265,9 @@ func newVerifiableMetric(metadata *pb.MetricMetadata, verifier *Verifier) (*veri if metadata.GetName() == "" || metadata.GetPrometheusName() == "" { return nil, errors.New("metric has no name") } + if strings.HasPrefix(metadata.GetPrometheusName(), MetaMetricPrefix) { + return nil, fmt.Errorf("metric name %q starts with %q which is a reserved prefix", metadata.GetPrometheusName(), "meta_") + } if !unicode.IsLower(rune(metadata.GetPrometheusName()[0])) { return nil, fmt.Errorf("invalid initial character in prometheus metric name: %q", metadata.GetPrometheusName()) } diff --git a/runsc/cmd/metric_server.go b/runsc/cmd/metric_server.go index 952d327d6..7512775b2 100644 --- a/runsc/cmd/metric_server.go +++ b/runsc/cmd/metric_server.go @@ -701,7 +701,7 @@ func (m *MetricServer) serveMetrics(w http.ResponseWriter, req *http.Request) ht // Meanwhile, build the map of all snapshots we will be rendering. snapshotsToOptions := make(map[*prometheus.Snapshot]prometheus.SnapshotExportOptions, numSandboxes+2) snapshotsToOptions[selfMetrics] = prometheus.SnapshotExportOptions{ - ExporterPrefix: fmt.Sprintf("%smeta_", m.exporterPrefix), + ExporterPrefix: fmt.Sprintf("%s%s", m.exporterPrefix, prometheus.MetaMetricPrefix), } processMetrics := prometheus.NewSnapshot() processMetrics.Add(prometheus.NewFloatData(&ProcessStartTimeMetric, float64(m.startTime.Unix())+(float64(m.startTime.Nanosecond())/1e9))) From bd30e8ba688c50e52e3e4aa18635d580fb746522 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Thu, 23 Mar 2023 12:06:40 -0700 Subject: [PATCH 41/49] Fix cmd.util.Infof args PiperOrigin-RevId: 518927601 --- runsc/cmd/util/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runsc/cmd/util/util.go b/runsc/cmd/util/util.go index b5cc6ef2a..140c582c7 100644 --- a/runsc/cmd/util/util.go +++ b/runsc/cmd/util/util.go @@ -48,8 +48,8 @@ func (i *Writer) Write(data []byte) (n int, err error) { // Infof writes message to log and stdout. func Infof(format string, args ...any) { - log.Infof(format, args) - fmt.Printf(format+"\n", args) + log.Infof(format, args...) + fmt.Printf(format+"\n", args...) } // Errorf logs error to containerd log (--log), to stderr, and debug logs. It From 68267dccc8f057e29624dcfd80274c804e0a372c Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Thu, 23 Mar 2023 14:21:44 -0700 Subject: [PATCH 42/49] `runsc`: Add metric counting calls of unimplemented system calls. This is useful to determine which syscalls users want but are unimplemented. PiperOrigin-RevId: 518962607 --- pkg/sentry/kernel/kernel.go | 1 + pkg/sentry/kernel/syscalls.go | 44 +++++++++++++++++++++++++++++++++ pkg/sentry/syscalls/syscalls.go | 1 + 3 files changed, 46 insertions(+) diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index be17dc39a..a8eac65fc 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -1540,6 +1540,7 @@ func (k *Kernel) EmitUnimplementedEvent(ctx context.Context, sysno uintptr) { }) t := TaskFromContext(ctx) + IncrementUnimplementedSyscallCounter(sysno) _, _ = k.unimplementedSyscallEmitter.Emit(&uspb.UnimplementedSyscall{ Tid: int32(t.ThreadID()), Registers: t.Arch().StateData().Proto(), diff --git a/pkg/sentry/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index 55fe2cf4c..fdf8b1c67 100644 --- a/pkg/sentry/kernel/syscalls.go +++ b/pkg/sentry/kernel/syscalls.go @@ -16,12 +16,14 @@ package kernel import ( "fmt" + "strconv" "google.golang.org/protobuf/proto" "gvisor.dev/gvisor/pkg/abi" "gvisor.dev/gvisor/pkg/atomicbitops" "gvisor.dev/gvisor/pkg/bits" "gvisor.dev/gvisor/pkg/hostarch" + "gvisor.dev/gvisor/pkg/metric" "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/seccheck" pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto" @@ -37,6 +39,10 @@ const ( // LINT.IfChange maxSyscallNum = 2000 // LINT.ThenChange(../seccheck/syscall.go) + + // outOfRangeSyscallNumber is used to represent a syscall number that is out of the + // range [0, maxSyscallNum] in monitoring. + outOfRangeSyscallNumber = "-1" ) // SyscallSupportLevel is a syscall support levels. @@ -347,6 +353,19 @@ func (s *SyscallTable) MaxSysno() (max uintptr) { // allSyscallTables contains all known tables. var allSyscallTables []*SyscallTable +var ( + // unimplementedSyscallCounterInit ensures the following fields are only initialized once. + unimplementedSyscallCounterInit sync.Once + + // unimplementedSyscallNumbers maps syscall numbers to their string representation. + // Used such that incrementing unimplementedSyscallCounter does not require allocating memory. + unimplementedSyscallNumbers map[uintptr]string + + // unimplementedSyscallCounter tracks the number of times each unimplemented syscall has been + // called by the sandboxed application. + unimplementedSyscallCounter *metric.Uint64Metric +) + // SyscallTables returns a read-only slice of registered SyscallTables. func SyscallTables() []*SyscallTable { return allSyscallTables @@ -371,6 +390,17 @@ func RegisterSyscallTable(s *SyscallTable) { panic(fmt.Sprintf("Duplicate SyscallTable registered for OS %v Arch %v", s.OS, s.Arch)) } allSyscallTables = append(allSyscallTables, s) + unimplementedSyscallCounterInit.Do(func() { + allowedValues := make([]string, maxSyscallNum+2) + unimplementedSyscallNumbers = make(map[uintptr]string, len(allowedValues)) + for i := uintptr(0); i <= maxSyscallNum; i++ { + s := strconv.Itoa(int(i)) + allowedValues[i] = s + unimplementedSyscallNumbers[i] = s + } + allowedValues[len(allowedValues)-1] = outOfRangeSyscallNumber + unimplementedSyscallCounter = metric.MustCreateNewUint64Metric("unimplemented_syscalls", true, "Number of times the application tried to call an unimplemented syscall, broken down by syscall number", metric.NewField("sysno", allowedValues)) + }) s.Init() } @@ -461,3 +491,17 @@ type SyscallInfo struct { Rval uintptr Errno int } + +// IncrementUnimplementedSyscallCounter increments the "unimplemented syscall" metric for the given +// syscall number. +// A syscall table must have been initialized prior to calling this function. +// +checkescape:all +// +//go:nosplit +func IncrementUnimplementedSyscallCounter(sysno uintptr) { + s, found := unimplementedSyscallNumbers[sysno] + if !found { + s = outOfRangeSyscallNumber + } + unimplementedSyscallCounter.Increment(s) +} diff --git a/pkg/sentry/syscalls/syscalls.go b/pkg/sentry/syscalls/syscalls.go index 7d5c9d8b3..a18eb0c9a 100644 --- a/pkg/sentry/syscalls/syscalls.go +++ b/pkg/sentry/syscalls/syscalls.go @@ -78,6 +78,7 @@ func Error(name string, err error, note string, urls []string) kernel.Syscall { return kernel.Syscall{ Name: name, Fn: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + kernel.IncrementUnimplementedSyscallCounter(sysno) return 0, nil, err }, SupportLevel: kernel.SupportUnimplemented, From f8b98248139cefd71afc5d3545dc0e8cdf305684 Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Thu, 23 Mar 2023 16:59:03 -0700 Subject: [PATCH 43/49] Update `unimpl.EmitUnimplementedEvent` interface to add the syscall number. 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 --- pkg/sentry/devices/tundev/tundev.go | 2 +- pkg/sentry/fsimpl/devpts/master.go | 8 +++--- pkg/sentry/fsimpl/devpts/replica.go | 4 +-- pkg/sentry/fsimpl/host/host.go | 4 +-- pkg/sentry/fsimpl/host/tty.go | 4 +-- pkg/sentry/fsimpl/overlay/regular_file.go | 4 +-- pkg/sentry/fsimpl/sys/kcov.go | 2 +- pkg/sentry/kernel/pipe/pipe_util.go | 2 +- pkg/sentry/kernel/pipe/vfs.go | 4 +-- pkg/sentry/socket/hostinet/socket.go | 4 +-- pkg/sentry/socket/hostinet/socket_unsafe.go | 2 +- pkg/sentry/socket/netlink/socket.go | 2 +- pkg/sentry/socket/netstack/netstack.go | 6 ++--- pkg/sentry/socket/unix/unix.go | 4 +-- pkg/sentry/syscalls/linux/sys_file.go | 2 +- pkg/sentry/unimpl/BUILD | 11 +++++++- pkg/sentry/unimpl/events.go | 6 ++--- pkg/sentry/unimpl/events_test.go | 28 ++++++++++++++++++++ pkg/sentry/vfs/file_description.go | 6 ++--- pkg/sentry/vfs/file_description_impl_util.go | 2 +- pkg/sentry/vfs/inotify.go | 2 +- pkg/sentry/vfs/opath.go | 2 +- 22 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 pkg/sentry/unimpl/events_test.go diff --git a/pkg/sentry/devices/tundev/tundev.go b/pkg/sentry/devices/tundev/tundev.go index c331e621d..92d289c22 100644 --- a/pkg/sentry/devices/tundev/tundev.go +++ b/pkg/sentry/devices/tundev/tundev.go @@ -69,7 +69,7 @@ type tunFD struct { } // Ioctl implements vfs.FileDescriptionImpl.Ioctl. -func (fd *tunFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (fd *tunFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { request := args[1].Uint() data := args[2].Pointer() diff --git a/pkg/sentry/fsimpl/devpts/master.go b/pkg/sentry/fsimpl/devpts/master.go index b3945499d..3153229b4 100644 --- a/pkg/sentry/fsimpl/devpts/master.go +++ b/pkg/sentry/fsimpl/devpts/master.go @@ -135,7 +135,7 @@ func (mfd *masterFileDescription) Write(ctx context.Context, src usermem.IOSeque } // Ioctl implements vfs.FileDescriptionImpl.Ioctl. -func (mfd *masterFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (mfd *masterFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { t := kernel.TaskFromContext(ctx) if t == nil { // ioctl(2) may only be called from a task goroutine. @@ -193,7 +193,7 @@ func (mfd *masterFileDescription) Ioctl(ctx context.Context, io usermem.IO, args } return 0, t.ThreadGroup().SetForegroundProcessGroupID(mfd.t.masterKTTY, kernel.ProcessGroupID(pgid)) default: - maybeEmitUnimplementedEvent(ctx, cmd) + maybeEmitUnimplementedEvent(ctx, sysno, cmd) return 0, linuxerr.ENOTTY } } @@ -212,7 +212,7 @@ func (mfd *masterFileDescription) Stat(ctx context.Context, opts vfs.StatOptions } // maybeEmitUnimplementedEvent emits unimplemented event if cmd is valid. -func maybeEmitUnimplementedEvent(ctx context.Context, cmd uint32) { +func maybeEmitUnimplementedEvent(ctx context.Context, sysno uintptr, cmd uint32) { switch cmd { case linux.TCGETS, linux.TCSETS, @@ -244,6 +244,6 @@ func maybeEmitUnimplementedEvent(ctx context.Context, cmd uint32) { linux.TIOCSSERIAL, linux.TIOCGPTPEER: - unimpl.EmitUnimplementedEvent(ctx) + unimpl.EmitUnimplementedEvent(ctx, sysno) } } diff --git a/pkg/sentry/fsimpl/devpts/replica.go b/pkg/sentry/fsimpl/devpts/replica.go index 98c2e5806..cac44b778 100644 --- a/pkg/sentry/fsimpl/devpts/replica.go +++ b/pkg/sentry/fsimpl/devpts/replica.go @@ -148,7 +148,7 @@ func (rfd *replicaFileDescription) Write(ctx context.Context, src usermem.IOSequ } // Ioctl implements vfs.FileDescriptionImpl.Ioctl. -func (rfd *replicaFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (rfd *replicaFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { t := kernel.TaskFromContext(ctx) if t == nil { // ioctl(2) may only be called from a task goroutine. @@ -199,7 +199,7 @@ func (rfd *replicaFileDescription) Ioctl(ctx context.Context, io usermem.IO, arg } return 0, t.ThreadGroup().SetForegroundProcessGroupID(rfd.inode.t.replicaKTTY, kernel.ProcessGroupID(pgid)) default: - maybeEmitUnimplementedEvent(ctx, cmd) + maybeEmitUnimplementedEvent(ctx, sysno, cmd) return 0, linuxerr.ENOTTY } } diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go index e1b5788d2..b32d2be8d 100644 --- a/pkg/sentry/fsimpl/host/host.go +++ b/pkg/sentry/fsimpl/host/host.go @@ -967,7 +967,7 @@ func (f *fileDescription) Epollable() bool { } // Ioctl queries the underlying FD for allowed ioctl commands. -func (f *fileDescription) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (f *fileDescription) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { switch cmd := args[1].Int(); cmd { case linux.FIONREAD: v, err := ioctlFionread(f.inode.hostFD) @@ -981,5 +981,5 @@ func (f *fileDescription) Ioctl(ctx context.Context, uio usermem.IO, args arch.S return 0, err } - return f.FileDescriptionDefaultImpl.Ioctl(ctx, uio, args) + return f.FileDescriptionDefaultImpl.Ioctl(ctx, uio, sysno, args) } diff --git a/pkg/sentry/fsimpl/host/tty.go b/pkg/sentry/fsimpl/host/tty.go index 04ac73255..63fb30c5d 100644 --- a/pkg/sentry/fsimpl/host/tty.go +++ b/pkg/sentry/fsimpl/host/tty.go @@ -144,7 +144,7 @@ func (t *TTYFileDescription) Write(ctx context.Context, src usermem.IOSequence, } // Ioctl implements vfs.FileDescriptionImpl.Ioctl. -func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { task := kernel.TaskFromContext(ctx) if task == nil { return 0, linuxerr.ENOTTY @@ -299,7 +299,7 @@ func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch linux.TIOCSSERIAL, linux.TIOCGPTPEER: - unimpl.EmitUnimplementedEvent(ctx) + unimpl.EmitUnimplementedEvent(ctx, sysno) fallthrough default: return 0, linuxerr.ENOTTY diff --git a/pkg/sentry/fsimpl/overlay/regular_file.go b/pkg/sentry/fsimpl/overlay/regular_file.go index 2e6f5d255..084485533 100644 --- a/pkg/sentry/fsimpl/overlay/regular_file.go +++ b/pkg/sentry/fsimpl/overlay/regular_file.go @@ -365,13 +365,13 @@ func (fd *regularFileFD) Sync(ctx context.Context) error { } // Ioctl implements vfs.FileDescriptionImpl.Ioctl. -func (fd *regularFileFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (fd *regularFileFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { wrappedFD, err := fd.getCurrentFD(ctx) if err != nil { return 0, err } defer wrappedFD.DecRef(ctx) - return wrappedFD.Ioctl(ctx, uio, args) + return wrappedFD.Ioctl(ctx, uio, sysno, args) } // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap. diff --git a/pkg/sentry/fsimpl/sys/kcov.go b/pkg/sentry/fsimpl/sys/kcov.go index cde68ac79..24846a0a5 100644 --- a/pkg/sentry/fsimpl/sys/kcov.go +++ b/pkg/sentry/fsimpl/sys/kcov.go @@ -75,7 +75,7 @@ type kcovFD struct { } // Ioctl implements vfs.FileDescriptionImpl.Ioctl. -func (fd *kcovFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (fd *kcovFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { cmd := uint32(args[1].Int()) arg := args[2].Uint64() switch uint32(cmd) { diff --git a/pkg/sentry/kernel/pipe/pipe_util.go b/pkg/sentry/kernel/pipe/pipe_util.go index 2c3a0fff0..ebafd3959 100644 --- a/pkg/sentry/kernel/pipe/pipe_util.go +++ b/pkg/sentry/kernel/pipe/pipe_util.go @@ -135,7 +135,7 @@ func (p *Pipe) Readiness(mask waiter.EventMask) waiter.EventMask { } // Ioctl implements ioctls on the Pipe. -func (p *Pipe) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (p *Pipe) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { // Switch on ioctl request. switch int(args[1].Int()) { case linux.FIONREAD: diff --git a/pkg/sentry/kernel/pipe/vfs.go b/pkg/sentry/kernel/pipe/vfs.go index a8320cf83..3d58249b7 100644 --- a/pkg/sentry/kernel/pipe/vfs.go +++ b/pkg/sentry/kernel/pipe/vfs.go @@ -234,8 +234,8 @@ func (fd *VFSPipeFD) Write(ctx context.Context, src usermem.IOSequence, _ vfs.Wr } // Ioctl implements vfs.FileDescriptionImpl.Ioctl. -func (fd *VFSPipeFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { - return fd.pipe.Ioctl(ctx, uio, args) +func (fd *VFSPipeFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { + return fd.pipe.Ioctl(ctx, uio, sysno, args) } // PipeSize implements fcntl(F_GETPIPE_SZ). diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go index 806055d8e..c457cd427 100644 --- a/pkg/sentry/socket/hostinet/socket.go +++ b/pkg/sentry/socket/hostinet/socket.go @@ -162,8 +162,8 @@ func (s *Socket) Epollable() bool { } // Ioctl implements vfs.FileDescriptionImpl. -func (s *Socket) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { - return ioctl(ctx, s.fd, uio, args) +func (s *Socket) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { + return ioctl(ctx, s.fd, uio, sysno, args) } // PRead implements vfs.FileDescriptionImpl.PRead. diff --git a/pkg/sentry/socket/hostinet/socket_unsafe.go b/pkg/sentry/socket/hostinet/socket_unsafe.go index a6a03f70c..1370ca59b 100644 --- a/pkg/sentry/socket/hostinet/socket_unsafe.go +++ b/pkg/sentry/socket/hostinet/socket_unsafe.go @@ -55,7 +55,7 @@ func writev(fd int, srcs []unix.Iovec) (uint64, error) { return uint64(n), nil } -func ioctl(ctx context.Context, fd int, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func ioctl(ctx context.Context, fd int, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { switch cmd := uintptr(args[1].Int()); cmd { case unix.TIOCINQ, unix.TIOCOUTQ: var val int32 diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go index 9eb5c5a3c..ef82f7c7b 100644 --- a/pkg/sentry/socket/netlink/socket.go +++ b/pkg/sentry/socket/netlink/socket.go @@ -166,7 +166,7 @@ func (s *Socket) Epollable() bool { } // Ioctl implements vfs.FileDescriptionImpl. -func (*Socket) Ioctl(context.Context, usermem.IO, arch.SyscallArguments) (uintptr, error) { +func (*Socket) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { // TODO(b/68878065): no ioctls supported. return 0, linuxerr.ENOTTY } diff --git a/pkg/sentry/socket/netstack/netstack.go b/pkg/sentry/socket/netstack/netstack.go index 06d1278b7..b0a3523b6 100644 --- a/pkg/sentry/socket/netstack/netstack.go +++ b/pkg/sentry/socket/netstack/netstack.go @@ -2974,7 +2974,7 @@ func (s *sock) SendMsg(t *kernel.Task, src usermem.IOSequence, to []byte, flags } // Ioctl implements vfs.FileDescriptionImpl. -func (s *sock) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (s *sock) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { t := kernel.TaskFromContext(ctx) if t == nil { panic("ioctl(2) may only be called from a task goroutine") @@ -3011,11 +3011,11 @@ func (s *sock) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArgum return 0, err } - return Ioctl(ctx, s.Endpoint, uio, args) + return Ioctl(ctx, s.Endpoint, uio, sysno, args) } // Ioctl performs a socket ioctl. -func Ioctl(ctx context.Context, ep commonEndpoint, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func Ioctl(ctx context.Context, ep commonEndpoint, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { t := kernel.TaskFromContext(ctx) if t == nil { panic("ioctl(2) may only be called from a task goroutine") diff --git a/pkg/sentry/socket/unix/unix.go b/pkg/sentry/socket/unix/unix.go index 485ebe746..820bef6d5 100644 --- a/pkg/sentry/socket/unix/unix.go +++ b/pkg/sentry/socket/unix/unix.go @@ -271,8 +271,8 @@ func (s *Socket) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error { } // Ioctl implements vfs.FileDescriptionImpl. -func (s *Socket) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { - return netstack.Ioctl(ctx, s.ep, uio, args) +func (s *Socket) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { + return netstack.Ioctl(ctx, s.ep, uio, sysno, args) } // PRead implements vfs.FileDescriptionImpl. diff --git a/pkg/sentry/syscalls/linux/sys_file.go b/pkg/sentry/syscalls/linux/sys_file.go index 292ab9e72..82ae9515c 100644 --- a/pkg/sentry/syscalls/linux/sys_file.go +++ b/pkg/sentry/syscalls/linux/sys_file.go @@ -283,7 +283,7 @@ func Ioctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, return 0, nil, setAsyncOwner(t, int(fd), file, ownerType, who) } - ret, err := file.Ioctl(t, t.MemoryManager(), args) + ret, err := file.Ioctl(t, t.MemoryManager(), sysno, args) return ret, nil, err } diff --git a/pkg/sentry/unimpl/BUILD b/pkg/sentry/unimpl/BUILD index 5abc2a0a7..15945e6fe 100644 --- a/pkg/sentry/unimpl/BUILD +++ b/pkg/sentry/unimpl/BUILD @@ -1,4 +1,4 @@ -load("//tools:defs.bzl", "go_library", "proto_library") +load("//tools:defs.bzl", "go_library", "go_test", "proto_library") package( default_applicable_licenses = ["//:license"], @@ -21,3 +21,12 @@ go_library( "//pkg/log", ], ) + +go_test( + name = "events_test", + srcs = ["events_test.go"], + deps = [ + ":unimpl", + "//pkg/sentry/kernel", + ], +) diff --git a/pkg/sentry/unimpl/events.go b/pkg/sentry/unimpl/events.go index 73ed9372f..4b79e4e98 100644 --- a/pkg/sentry/unimpl/events.go +++ b/pkg/sentry/unimpl/events.go @@ -31,15 +31,15 @@ const ( // Events interface defines method to emit unsupported events. type Events interface { - EmitUnimplementedEvent(context.Context) + EmitUnimplementedEvent(ctx context.Context, sysno uintptr) } // EmitUnimplementedEvent emits unsupported syscall event to the context. -func EmitUnimplementedEvent(ctx context.Context) { +func EmitUnimplementedEvent(ctx context.Context, sysno uintptr) { e := ctx.Value(CtxEvents) if e == nil { log.Warningf("Context.Value(CtxEvents) not present, unimplemented syscall event not reported.") return } - e.(Events).EmitUnimplementedEvent(ctx) + e.(Events).EmitUnimplementedEvent(ctx, sysno) } diff --git a/pkg/sentry/unimpl/events_test.go b/pkg/sentry/unimpl/events_test.go new file mode 100644 index 000000000..91b6e89dc --- /dev/null +++ b/pkg/sentry/unimpl/events_test.go @@ -0,0 +1,28 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package events_test verifies that kernel.Kernel implements interface unimpl.Events. +package events_test + +import ( + "testing" + + "gvisor.dev/gvisor/pkg/sentry/kernel" + "gvisor.dev/gvisor/pkg/sentry/unimpl" +) + +// TestInterfaceMatch verifies that kernel.Kernel implements interface unimpl.Events. +func TestInterfaceMatch(t *testing.T) { + var _ = (unimpl.Events)((*kernel.Kernel)(nil)) +} diff --git a/pkg/sentry/vfs/file_description.go b/pkg/sentry/vfs/file_description.go index a8c00e372..fb6a3cc75 100644 --- a/pkg/sentry/vfs/file_description.go +++ b/pkg/sentry/vfs/file_description.go @@ -446,7 +446,7 @@ type FileDescriptionImpl interface { ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error // Ioctl implements the ioctl(2) syscall. - Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) + Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) // ListXattr returns all extended attribute names for the file. ListXattr(ctx context.Context, size uint64) ([]string, error) @@ -708,8 +708,8 @@ func (fd *FileDescription) ConfigureMMap(ctx context.Context, opts *memmap.MMapO } // Ioctl implements the ioctl(2) syscall. -func (fd *FileDescription) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { - return fd.impl.Ioctl(ctx, uio, args) +func (fd *FileDescription) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { + return fd.impl.Ioctl(ctx, uio, sysno, args) } // ListXattr returns all extended attribute names for the file represented by diff --git a/pkg/sentry/vfs/file_description_impl_util.go b/pkg/sentry/vfs/file_description_impl_util.go index b47d0c1b9..499d9fed3 100644 --- a/pkg/sentry/vfs/file_description_impl_util.go +++ b/pkg/sentry/vfs/file_description_impl_util.go @@ -143,7 +143,7 @@ func (FileDescriptionDefaultImpl) ConfigureMMap(ctx context.Context, opts *memma // Ioctl implements FileDescriptionImpl.Ioctl analogously to // file_operations::unlocked_ioctl == NULL in Linux. -func (FileDescriptionDefaultImpl) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (FileDescriptionDefaultImpl) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { return 0, linuxerr.ENOTTY } diff --git a/pkg/sentry/vfs/inotify.go b/pkg/sentry/vfs/inotify.go index aea54a5d5..85f018ccf 100644 --- a/pkg/sentry/vfs/inotify.go +++ b/pkg/sentry/vfs/inotify.go @@ -253,7 +253,7 @@ func (i *Inotify) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOpt } // Ioctl implements FileDescriptionImpl.Ioctl. -func (i *Inotify) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (i *Inotify) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { switch args[1].Int() { case linux.FIONREAD: i.evMu.Lock() diff --git a/pkg/sentry/vfs/opath.go b/pkg/sentry/vfs/opath.go index df51bb227..ec8084915 100644 --- a/pkg/sentry/vfs/opath.go +++ b/pkg/sentry/vfs/opath.go @@ -64,7 +64,7 @@ func (fd *opathFD) Write(ctx context.Context, src usermem.IOSequence, opts Write } // Ioctl implements FileDescriptionImpl.Ioctl. -func (fd *opathFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { +func (fd *opathFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { return 0, linuxerr.EBADF } From d0326a67dab2a302f74fb83c7d1a4f72906df8c3 Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Thu, 23 Mar 2023 17:09:41 -0700 Subject: [PATCH 44/49] `runsc`: Refactor in how the version string is propagated in `runsc`. This is helpful so that it can be imported form other packages without import loops. This will be used in a follow-up change to add the version string as a per-sandbox metric metadata label. PiperOrigin-RevId: 519002695 --- runsc/BUILD | 16 ++++++++++------ runsc/cli/BUILD | 1 + runsc/cli/main.go | 7 ++++--- runsc/main.go | 7 ++++++- runsc/version/BUILD | 12 ++++++++++++ runsc/{ => version}/version.go | 15 ++++++++++++--- runsc/version_test.sh | 6 +++--- 7 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 runsc/version/BUILD rename runsc/{ => version}/version.go (61%) diff --git a/runsc/BUILD b/runsc/BUILD index f85e6d3b8..a79241cb5 100644 --- a/runsc/BUILD +++ b/runsc/BUILD @@ -9,15 +9,17 @@ go_binary( name = "runsc", srcs = [ "main.go", - "version.go", ], pure = True, tags = ["staging"], visibility = [ "//visibility:public", ], - x_defs = {"main.version": "{STABLE_VERSION}"}, - deps = ["//runsc/cli"], + x_defs = {"gvisor.dev/gvisor/runsc/version.version": "{STABLE_VERSION}"}, + deps = [ + "//runsc/cli", + "//runsc/version", + ], ) # The runsc-race target is a race-compatible BUILD target. This must be built @@ -37,15 +39,17 @@ go_binary( name = "runsc-race", srcs = [ "main.go", - "version.go", ], gotags = ["lockdep"], static = True, visibility = [ "//visibility:public", ], - x_defs = {"main.version": "{STABLE_VERSION}"}, - deps = ["//runsc/cli"], + x_defs = {"gvisor.dev/gvisor/runsc/version.version": "{STABLE_VERSION}"}, + deps = [ + "//runsc/cli", + "//runsc/version", + ], ) sh_test( diff --git a/runsc/cli/BUILD b/runsc/cli/BUILD index f4c8bb1a2..625a0aab0 100644 --- a/runsc/cli/BUILD +++ b/runsc/cli/BUILD @@ -24,6 +24,7 @@ go_library( "//runsc/config", "//runsc/flag", "//runsc/specutils", + "//runsc/version", "@com_github_google_subcommands//:go_default_library", "@org_golang_x_sys//unix:go_default_library", ], diff --git a/runsc/cli/main.go b/runsc/cli/main.go index 6518a9da2..f554e849d 100644 --- a/runsc/cli/main.go +++ b/runsc/cli/main.go @@ -38,6 +38,7 @@ import ( "gvisor.dev/gvisor/runsc/config" "gvisor.dev/gvisor/runsc/flag" "gvisor.dev/gvisor/runsc/specutils" + "gvisor.dev/gvisor/runsc/version" ) var ( @@ -56,7 +57,7 @@ var ( ) // Main is the main entrypoint. -func Main(version string) { +func Main() { // Help and flags commands are generated automatically. help := cmd.NewHelp(subcommands.DefaultCommander) help.Register(new(cmd.Platforms)) @@ -118,7 +119,7 @@ func Main(version string) { // Are we showing the version? if *showVersion { // The format here is the same as runc. - fmt.Fprintf(os.Stdout, "runsc version %s\n", version) + fmt.Fprintf(os.Stdout, "runsc version %s\n", version.Version()) fmt.Fprintf(os.Stdout, "spec: %s\n", specutils.Version) os.Exit(0) } @@ -221,7 +222,7 @@ func Main(version string) { log.Infof("***************************") log.Infof("Args: %s", os.Args) - log.Infof("Version %s", version) + log.Infof("Version %s", version.Version()) log.Infof("GOOS: %s", runtime.GOOS) log.Infof("GOARCH: %s", runtime.GOARCH) log.Infof("PID: %d", os.Getpid()) diff --git a/runsc/main.go b/runsc/main.go index 4ce5ebee9..1f89dd187 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -17,8 +17,13 @@ package main import ( "gvisor.dev/gvisor/runsc/cli" + "gvisor.dev/gvisor/runsc/version" ) +// version.Version is set dynamically, but needs to be +// linked in the binary, so reference it here. +var _ = version.Version() + func main() { - cli.Main(version) + cli.Main() } diff --git a/runsc/version/BUILD b/runsc/version/BUILD new file mode 100644 index 000000000..f3c9c0559 --- /dev/null +++ b/runsc/version/BUILD @@ -0,0 +1,12 @@ +load("//tools:defs.bzl", "go_library") + +package( + default_applicable_licenses = ["//:license"], + licenses = ["notice"], +) + +go_library( + name = "version", + srcs = ["version.go"], + visibility = ["//:sandbox"], +) diff --git a/runsc/version.go b/runsc/version/version.go similarity index 61% rename from runsc/version.go rename to runsc/version/version.go index c250f4a2a..16b3da3ff 100644 --- a/runsc/version.go +++ b/runsc/version/version.go @@ -1,4 +1,4 @@ -// Copyright 2019 The gVisor Authors. +// Copyright 2023 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,7 +15,16 @@ //go:build go1.1 // +build go1.1 -package main +// Package version holds a string containing version information for runsc. +// Other packages may import it to get this information while avoiding +// import loops. +package version -// version is set during linking. +// version is the version string. +// It is initialized by the runsc main() function. var version = "VERSION_MISSING" + +// Version returns the version string. +func Version() string { + return version +} diff --git a/runsc/version_test.sh b/runsc/version_test.sh index 747350654..510b40a4f 100755 --- a/runsc/version_test.sh +++ b/runsc/version_test.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2018 The gVisor Authors. +# Copyright 2023 The gVisor Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,8 +19,8 @@ set -euf -x -o pipefail readonly runsc="$1" readonly version=$($runsc --version) -# Version should should not match VERSION, which is the default and which will -# also appear if something is wrong with workspace_status.sh script. +# Version should should not match VERSION, which is the default and which +# will also appear if something is wrong with workspace_status.sh script. if [[ $version =~ "VERSION" ]]; then echo "FAIL: Got bad version $version" exit 1 From 1c9531cd53772386e4aa396a20ab899165ac1555 Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Thu, 23 Mar 2023 19:25:47 -0700 Subject: [PATCH 45/49] `runsc`: Add version information to sandbox metric metadata. This allows the runsc version to be tracked in monitoring. PiperOrigin-RevId: 519025313 --- runsc/config/BUILD | 1 + runsc/config/config.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/runsc/config/BUILD b/runsc/config/BUILD index 8db4bdb26..6b7471719 100644 --- a/runsc/config/BUILD +++ b/runsc/config/BUILD @@ -18,6 +18,7 @@ go_library( "//pkg/refs", "//pkg/sentry/watchdog", "//runsc/flag", + "//runsc/version", ], ) diff --git a/runsc/config/config.go b/runsc/config/config.go index bc238c324..9f2fdd794 100644 --- a/runsc/config/config.go +++ b/runsc/config/config.go @@ -28,6 +28,7 @@ import ( "gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/sentry/watchdog" "gvisor.dev/gvisor/runsc/flag" + "gvisor.dev/gvisor/runsc/version" ) // Config holds configuration that is not part of the runtime spec. @@ -391,6 +392,7 @@ func (b Bundle) Validate() error { // exported about the sandbox this config represents. func (c *Config) MetricMetadata() map[string]string { return map[string]string{ + "version": version.Version(), "platform": c.Platform, "network": c.Network.String(), "numcores": strconv.Itoa(runtime.NumCPU()), From 585533eae7721eaebab59a32f84b3a648d95fb88 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Fri, 24 Mar 2023 09:49:14 -0700 Subject: [PATCH 46/49] systrap: check that minimum one stub thread is active after queueing a context and don't activate more threads than contexts. PiperOrigin-RevId: 519168272 --- pkg/sentry/platform/systrap/context_queue.go | 10 +++-- pkg/sentry/platform/systrap/subprocess.go | 19 ++++++++- .../platform/systrap/sysmsg/sysmsg_lib.c | 39 +++++++++++++++---- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/pkg/sentry/platform/systrap/context_queue.go b/pkg/sentry/platform/systrap/context_queue.go index 985117544..285df86e4 100644 --- a/pkg/sentry/platform/systrap/context_queue.go +++ b/pkg/sentry/platform/systrap/context_queue.go @@ -45,8 +45,10 @@ type contextQueue struct { // stubPollingIndexBase is used by stubs to indicate to each other how many // threads went to sleep. stubPollingIndexBase uint32 - // numSleepingThreads indicates to the sentry how many stubs are asleep. - numSleepingThreads uint32 + // numActiveThreads indicates to the sentry how many stubs are running. + numActiveThreads uint32 + // numActiveContext is a number of running and waiting contexts + numActiveContexts uint32 // ringbuffer is the mmapped region of memory that's shared with the stub // threads. ringbuffer [maxContextQueueEntries]uint32 @@ -62,7 +64,8 @@ func (q *contextQueue) init() { atomic.StoreUint32(&q.end, 0) atomic.StoreUint32(&q.stubPollingIndex, 0) atomic.StoreUint32(&q.stubPollingIndexBase, 0) - atomic.StoreUint32(&q.numSleepingThreads, 0) + atomic.StoreUint32(&q.numActiveThreads, 0) + atomic.StoreUint32(&q.numActiveContexts, 0) } func (q *contextQueue) isEmpty() bool { @@ -74,6 +77,7 @@ func (q *contextQueue) queuedContexts() uint32 { } func (q *contextQueue) add(contextID uint32) uint32 { + atomic.AddUint32(&q.numActiveContexts, 1) next := atomic.AddUint32(&q.end, 1) if (next % maxContextQueueEntries) == (atomic.LoadUint32(&q.start) % maxContextQueueEntries) { diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index e819ab333..8abf3760b 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -315,7 +315,9 @@ func newSubprocess(create func() (*thread, error), memoryFile *pgalloc.MemoryFil // Create the initial sysmsg thread. if contextDecouplingExp { + atomic.AddUint32(&sp.contextQueue.numActiveThreads, 1) if _, err := sp.createSysmsgThread(nil, nil, nil); err != nil { + atomic.AddUint32(&sp.contextQueue.numActiveThreads, ^uint32(0)) return nil, err } sp.numSysmsgThreads++ @@ -799,6 +801,10 @@ func (s *subprocess) waitOnState(ctx *sharedContext) { slowPath := false start := cputicks() handshake := false + if atomic.LoadUint32(&s.contextQueue.numActiveThreads) == 0 { + kicked = true + s.kickSysmsgThread() + } for curState := ctx.state(); curState == sysmsg.ContextStateNone; curState = ctx.state() { if !slowPath { delta := uint64(cputicks() - start) @@ -832,7 +838,15 @@ func (s *subprocess) waitOnState(ctx *sharedContext) { func (s *subprocess) kickSysmsgThread() { s.sysmsgThreadsMu.Lock() - if atomic.LoadUint32(&s.contextQueue.numSleepingThreads) > 0 { + nrActiveContexts := atomic.LoadUint32(&s.contextQueue.numActiveContexts) + nrActiveThreads := atomic.LoadUint32(&s.contextQueue.numActiveThreads) + + if nrActiveThreads >= nrActiveContexts { + s.sysmsgThreadsMu.Unlock() + return + } + + if s.numSysmsgThreads > int(nrActiveThreads) { for _, t := range s.sysmsgThreads { if t.msg.State.Get() == sysmsg.ThreadStateAsleep { t.msg.WakeSysmsgThread() @@ -847,7 +861,10 @@ func (s *subprocess) kickSysmsgThread() { if s.numSysmsgThreads < maxSysmsgThreads { s.numSysmsgThreads++ s.sysmsgThreadsMu.Unlock() + atomic.AddUint32(&s.contextQueue.numActiveThreads, 1) if _, err := s.createSysmsgThread(nil, nil, nil); err != nil { + log.Warningf("Unable to create a new stub thread: %s", err) + atomic.AddUint32(&s.contextQueue.numActiveThreads, ^uint32(0)) s.sysmsgThreadsMu.Lock() s.numSysmsgThreads-- s.sysmsgThreadsMu.Unlock() diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c index d8b707ea2..32021e678 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c @@ -41,7 +41,8 @@ struct context_queue { uint32_t end; uint32_t polling_index; uint32_t polling_index_base; - uint32_t num_sleeping_threads; + uint32_t num_active_threads; + uint32_t num_active_contexts; uint32_t ringbuffer[MAX_CONTEXT_QUEUE_ENTRIES]; }; @@ -209,22 +210,43 @@ struct thread_context *get_context(struct sysmsg *sysmsg) { __atomic_store_n(&sysmsg->state, THREAD_STATE_PREP, __ATOMIC_RELEASE); ctx = queue_get_context(sysmsg); if (ctx) return ctx; + if (spinning_queue_push()) { - while (!spinning_queue_remove_first(__export_deep_sleep_timeout)) { + while (1) { ctx = queue_get_context(sysmsg); if (ctx) { spinning_queue_pop(); return ctx; } - + if (spinning_queue_remove_first(__export_deep_sleep_timeout)) { + break; + } spinloop(); } } - __atomic_store_n(&sysmsg->state, THREAD_STATE_ASLEEP, __ATOMIC_RELEASE); + uint32_t nr_active_threads = + __atomic_sub_fetch(&queue->num_active_threads, 1, __ATOMIC_ACQ_REL); + uint32_t nr_active_contexts = + __atomic_load_n(&queue->num_active_contexts, __ATOMIC_ACQUIRE); + // We have to make another attempt to get a context here to prevent TOCTTOU + // races with waitOnState and kickSysmsgThread. There are two assumptions: + // * If the queue isn't empty, one or more threads have to be active. + // * A new thread isn't kicked, if the number of active threads are not less + // than a number of active contexts. + if (nr_active_threads == 0 || nr_active_threads < nr_active_contexts) { + ctx = queue_get_context(sysmsg); + if (ctx) { + __atomic_add_fetch(&queue->num_active_threads, 1, __ATOMIC_ACQ_REL); + return ctx; + } + } - __atomic_add_fetch(&queue->num_sleeping_threads, 1, __ATOMIC_ACQ_REL); - sys_futex(&sysmsg->state, FUTEX_WAIT, THREAD_STATE_ASLEEP, NULL, NULL, 0); - __atomic_sub_fetch(&queue->num_sleeping_threads, 1, __ATOMIC_ACQ_REL); + __atomic_store_n(&sysmsg->state, THREAD_STATE_ASLEEP, __ATOMIC_RELEASE); + while (__atomic_load_n(&sysmsg->state, __ATOMIC_ACQUIRE) == + THREAD_STATE_ASLEEP) { + sys_futex(&sysmsg->state, FUTEX_WAIT, THREAD_STATE_ASLEEP, NULL, NULL, 0); + } + __atomic_add_fetch(&queue->num_active_threads, 1, __ATOMIC_ACQ_REL); } } @@ -233,6 +255,8 @@ struct thread_context *get_context(struct sysmsg *sysmsg) { struct thread_context *switch_context(struct sysmsg *sysmsg, struct thread_context *ctx, enum context_state new_context_state) { + struct context_queue *queue = __export_context_queue_addr; + __atomic_store_n(&ctx->thread_id, INVALID_THREAD_ID, __ATOMIC_RELEASE); __atomic_store_n(&ctx->last_thread_id, sysmsg->thread_id, __ATOMIC_RELEASE); __atomic_store_n(&ctx->state, new_context_state, __ATOMIC_RELEASE); @@ -242,6 +266,7 @@ struct thread_context *switch_context(struct sysmsg *sysmsg, panic(ret); } } + __atomic_sub_fetch(&queue->num_active_contexts, 1, __ATOMIC_ACQ_REL); struct thread_context *old_ctx = sysmsg->context; From 588b4e13ce2b168cda0fbe16027b0e5bf801f92f Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Fri, 24 Mar 2023 13:04:55 -0700 Subject: [PATCH 47/49] github: update OSS schemastore This was done internally in cl/518959241. PiperOrigin-RevId: 519218468 --- WORKSPACE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 1461c05e2..0f3a99090 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1485,8 +1485,8 @@ http_file( http_file( name = "github_workflow_schema", - sha256 = "60603d1095b11d136e04a8b95be83a23ad8044169e46f82f925c320c1cf47a49", - urls = ["https://raw.githubusercontent.com/SchemaStore/schemastore/27612065234778feaac216ce14dd47846fe0a2dd/src/schemas/json/github-workflow.json"], + sha256 = "7499ccb3e75975504ea1ee7c70291e0c9f6c1f684678091d013061fe263e3ddb", + urls = ["https://raw.githubusercontent.com/SchemaStore/schemastore/166136b96a14f103a948053903e9339e63ad9170/src/schemas/json/github-workflow.json"], ) # External Go repositories. From 000b5b904a0096f945d34057f81359aade4b6587 Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Fri, 24 Mar 2023 15:07:01 -0700 Subject: [PATCH 48/49] `runsc`: Differentiate between `directfs` and non-`directfs` in metric labels. This renames the `gofermode` metadata metric label to `fsmode`, which is either "default" or "directfs". PiperOrigin-RevId: 519247987 --- runsc/config/config.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/runsc/config/config.go b/runsc/config/config.go index 9f2fdd794..f1981e3b9 100644 --- a/runsc/config/config.go +++ b/runsc/config/config.go @@ -391,15 +391,19 @@ func (b Bundle) Validate() error { // MetricMetadata returns key-value pairs that are useful to include in metrics // exported about the sandbox this config represents. func (c *Config) MetricMetadata() map[string]string { + var fsMode = "goferfs" + if c.DirectFS { + fsMode = "directfs" + } return map[string]string{ - "version": version.Version(), - "platform": c.Platform, - "network": c.Network.String(), - "numcores": strconv.Itoa(runtime.NumCPU()), - "coretags": strconv.FormatBool(c.EnableCoreTags), - "overlay": c.Overlay2.String(), - "gofermode": "default", - "cpuarch": runtime.GOARCH, + "version": version.Version(), + "platform": c.Platform, + "network": c.Network.String(), + "numcores": strconv.Itoa(runtime.NumCPU()), + "coretags": strconv.FormatBool(c.EnableCoreTags), + "overlay": c.Overlay2.String(), + "fsmode": fsMode, + "cpuarch": runtime.GOARCH, } } From b250ee717d8797a27ca31cc4ad43e10ba59d88f1 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Fri, 24 Mar 2023 16:08:49 -0700 Subject: [PATCH 49/49] github: remove update styfle/cancel-workflow-action version We're on a to-be-deprecated version (see bug), and GitHub now provides cancellation built-in. See docs here: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency PiperOrigin-RevId: 519261554 --- .github/workflows/go.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 0d6eaa6df..98781e5df 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,14 +12,14 @@ name: "Go" - master - "feature/**" +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: generate: runs-on: ubuntu-latest steps: - - name: Cancel previous - uses: styfle/cancel-workflow-action@0.7.0 - with: - access_token: ${{ github.token }} - id: setup run: | if ! [[ -z "${{ secrets.GO_TOKEN }}" ]]; then