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/.github/workflows/go.yml b/.github/workflows/go.yml index b0c90df4b..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.11.0 - with: - access_token: ${{ github.token }} - id: setup run: | if ! [[ -z "${{ secrets.GO_TOKEN }}" ]]; 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/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. 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/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"] 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/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/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. 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 { 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/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/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/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/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..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: @@ -490,7 +492,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 74e910a27..fdbfee371 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,76 +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(bsFD); err != nil { - bsFD.Close(ctx) + 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 @@ -507,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 @@ -591,41 +604,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..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}) @@ -1753,12 +1752,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..ea807e86c 100644 --- a/pkg/sentry/fsimpl/gofer/gofer.go +++ b/pkg/sentry/fsimpl/gofer/gofer.go @@ -73,26 +73,24 @@ 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" - moptHostUDSConnect = "host_uds_connect" - moptHostUDSBind = "host_uds_bind" + moptDirectfs = "directfs" ) // Valid values for the "cache" mount option. const ( - cacheNone = "none" cacheFSCache = "fscache" cacheFSCacheWritethrough = "fscache_writethrough" cacheRemoteRevalidating = "remote_revalidating" @@ -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 @@ -432,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: @@ -469,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 @@ -485,14 +476,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/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/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/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/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/kernel.go b/pkg/sentry/kernel/kernel.go index 260fcc919..a8eac65fc 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -1534,12 +1534,13 @@ 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) }) 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/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/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index fe9877a1f..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. @@ -90,7 +96,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) @@ -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/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.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. 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 } 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/platform/systrap/BUILD b/pkg/sentry/platform/systrap/BUILD index 946beeeb6..b48dc9e42 100644 --- a/pkg/sentry/platform/systrap/BUILD +++ b/pkg/sentry/platform/systrap/BUILD @@ -22,20 +22,20 @@ go_library( srcs = [ "context_decoupling_disable.go", "context_decoupling_enable.go", + "context_queue.go", "filters.go", "filters_amd64.go", "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/context_queue.go b/pkg/sentry/platform/systrap/context_queue.go new file mode 100644 index 000000000..285df86e4 --- /dev/null +++ b/pkg/sentry/platform/systrap/context_queue.go @@ -0,0 +1,90 @@ +// 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 + // 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 +} + +// 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.numActiveThreads, 0) + atomic.StoreUint32(&q.numActiveContexts, 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 { + atomic.AddUint32(&q.numActiveContexts, 1) + 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/shared_context.go b/pkg/sentry/platform/systrap/shared_context.go new file mode 100644 index 000000000..d867ab3d2 --- /dev/null +++ b/pkg/sentry/platform/systrap/shared_context.go @@ -0,0 +1,169 @@ +// 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 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 + // 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: uint32(id), + shared: s.getThreadContextFromID(id), + } + sc.shared.Init(invalidThreadID) + + return &sc, nil +} + +func (sc *sharedContext) release() { + if sc == nil { + return + } + sc.subprocess.threadContextPool.Put(uint64(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 + 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) { + 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/stub_unsafe.go b/pkg/sentry/platform/systrap/stub_unsafe.go index d87494ade..51e389ce4 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 | // *------------------------------------* @@ -131,10 +134,22 @@ 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. mapLen, _ = hostarch.PageRoundUp(mapLen + sysmsg.PerThreadMemSize*(maxSystemThreads+1)) + + // Allocate context queue region + if contextDecouplingExp { + stubContextQueueRegion = mapLen + stubContextQueueRegionLen, _ = hostarch.PageRoundUp(unsafe.Sizeof(contextQueue{})) + mapLen += stubContextQueueRegionLen + + stubSpinningThreadQueueAddr = mapLen + mapLen += sysmsg.SpinningQueueMemSize + } + // Allocate thread context region stubContextRegion = mapLen stubContextRegionLen = sysmsg.AllocatedSizeofThreadContextStruct * (maxGuestContexts + 1) @@ -177,6 +192,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 +203,8 @@ func stubInit() { stubSysmsgStart += stubStart stubSysmsgStack += stubStart stubROMapEnd += stubStart + stubContextQueueRegion += stubStart + stubSpinningThreadQueueAddr += stubStart stubContextRegion += stubStart // Align stubSysmsgStack to the per-thread stack size. @@ -204,11 +222,18 @@ 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))) if contextDecouplingExp { *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) @@ -224,7 +249,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..8abf3760b 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 @@ -290,6 +311,17 @@ 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 { + 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++ + } return sp, nil } @@ -301,7 +333,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 +342,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 +380,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)}, @@ -343,6 +394,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). @@ -629,70 +696,184 @@ 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. + // 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 } - 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) + // 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) { + 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() + defer func() { + ctx.clearInterrupt() + c.interrupt.Disable() + }() - msg.EnableSentryFastPath() - sysThread.waitEvent(sysmsg.ThreadStateDone) + if contextDecouplingExp { + 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.setState(sysmsg.ContextStateNone) + s.contextQueue.add(uint32(ctx.contextID)) + s.waitOnState(ctx) - regs.PtraceRegs = ctx.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 !contextDecouplingExp && ctx.State != sysmsg.ContextStateSyscallTrap { - var err error - sysThread.fpuStateToMsgOffset, err = msg.FPUStateOffset() - if err != nil { - return false, false, err + // Check if there's been an error. + 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, threadID) + } + } else { + msg := sysThread.msg + t := sysThread.thread + + restoreFPState(msg, ctx, sysThread.fpuStateToMsgOffset, c, ac) + + msg.EnableSentryFastPath() + sysThread.waitEvent(sysmsg.ThreadStateDone, ctx) + + // 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(regs, msg, t, ac) - - if ctx.State == sysmsg.ContextStateSyscallCanBePatched { - ctx.State = sysmsg.ContextStateSyscall + // 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. + 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 } +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 *sharedContext) { + kicked := false + 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) + if delta > decoupledDeepSleepTimeout { + ctx.disableSentryFastPath() + slowPath = true + continue + } + + if !handshake && ctx.isAcked() { + 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) + } + } + + ctx.resetAcked() + ctx.enableSentryFastPath() +} + +func (s *subprocess) kickSysmsgThread() { + s.sysmsgThreadsMu.Lock() + + 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() + 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() + 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() + } + } 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() @@ -749,20 +930,27 @@ 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) - sysThread, err := s.getSysmsgThread(&ac.StateData().Regs, c, ac) - if err != nil { - return err + if contextDecouplingExp { + saveFPState(nil, c.sharedContext, 0, c, ac) + } else { + sysThread, err := s.getSysmsgThread(&ac.StateData().Regs, c, ac) + if err != nil { + return err + } + saveFPState(sysThread.msg, c.sharedContext, 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 +961,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 +1004,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 +1054,18 @@ 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.Context = 0 + } else { + c.sharedContext.setThreadID(threadID) + 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.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 +1085,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 +1100,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, c.sharedContext) + 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 @@ -931,59 +1134,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 - 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.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_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.go b/pkg/sentry/platform/systrap/subprocess_arm64.go index ae6f0bf9f..21db37029 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,29 @@ 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. 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())) + } } -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_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 5db549ccb..3f7a3d44e 100644 --- a/pkg/sentry/platform/systrap/subprocess_unsafe.go +++ b/pkg/sentry/platform/systrap/subprocess_unsafe.go @@ -22,8 +22,13 @@ package systrap import ( + "fmt" "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" ) @@ -45,3 +50,62 @@ 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)) +} + +//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/BUILD b/pkg/sentry/platform/systrap/sysmsg/BUILD index 44ac4fc54..9b4d15466 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,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/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 c42d12399..0d9ee1d78 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,37 +155,64 @@ 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); - struct thread_context *ctx = thread_context_addr(sysmsg); + 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 = sysmsg->context; 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. - // 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) { @@ -221,6 +239,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); @@ -235,9 +254,7 @@ 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; + 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 +309,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 +327,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, @@ -340,24 +352,25 @@ 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; - 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 *unused) { + 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..18fb594bb 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); - struct thread_context *ctx = thread_context_addr(sysmsg); + 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 = sysmsg->context; + + 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,49 @@ 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 = + (struct fpsimd_context *)&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..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) @@ -196,8 +187,11 @@ __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 + 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 d2ec4accb..521654d00 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. @@ -112,6 +115,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. @@ -139,16 +152,10 @@ 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 + // Context is a pointer to the ThreadContext struct that the current sysmsg + // thread is processing. + Context uint64 - // 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 @@ -162,9 +169,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 +231,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 +269,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 +296,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,11 +321,15 @@ 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) } @@ -324,7 +348,8 @@ 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) + fmt.Fprintf(&b, " ThreadID %d", m.ThreadID) b.WriteString("}") return b.String() @@ -336,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.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h index 3dd62bbd9..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 @@ -37,14 +38,19 @@ 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, }; +struct thread_context; + // sysmsg contains the current state of the sysmsg thread. See: sysmsg.go:Msg struct sysmsg { struct sysmsg *self; @@ -54,19 +60,15 @@ struct sysmsg { uint64_t app_stack; uint32_t interrupt; uint32_t state; - uint64_t context_id; - uint64_t context_region; + struct thread_context *context; // 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; 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 +98,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; }; @@ -106,6 +112,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. @@ -118,6 +125,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; +struct context_queue; +extern struct context_queue *__export_context_queue_addr; // NOLINTBEGIN(runtime/int) static void *sysmsg_sp() { @@ -135,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; @@ -151,10 +153,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 = sysmsg->context; 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 +172,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_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/sysmsg/sysmsg_lib.c b/pkg/sentry/platform/systrap/sysmsg/sysmsg_lib.c index 358f3856f..32021e678 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 @@ -28,24 +29,38 @@ uint64_t __export_deep_sleep_timeout; uint64_t __export_handshake_timeout; -// 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_active_threads; + uint32_t num_active_contexts; + 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) { + 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) { @@ -65,13 +80,221 @@ 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]; } } -int wait_state(struct sysmsg *sysmsg, uint32_t state) { +// 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); + } + 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; + } + 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 = __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); + ctx = queue_get_context(sysmsg); + if (ctx) return ctx; + + if (spinning_queue_push()) { + 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(); + } + } + 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_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); + } +} + +// 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) { + 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); + 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); + } + } + __atomic_sub_fetch(&queue->num_active_contexts, 1, __ATOMIC_ACQ_REL); + + struct thread_context *old_ctx = sysmsg->context; + + ctx = get_context(sysmsg); + + if (old_ctx != ctx || 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 +304,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) { @@ -141,10 +364,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 0c0ec5514..5daed1bbd 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 @@ -39,8 +39,7 @@ #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 0x30 #define offsetof_thread_context_fpstate 0x0 #define offsetof_thread_context_fpstate_changed MAX_FPSTATE_LEN 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..7ff0eab95 --- /dev/null +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg_unsafe.go @@ -0,0 +1,88 @@ +// 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 ( + "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" +) + +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. +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..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" ) @@ -47,10 +48,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 @@ -120,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) @@ -131,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() } diff --git a/pkg/sentry/platform/systrap/systrap.go b/pkg/sentry/platform/systrap/systrap.go index 4fa37ec89..28d8070a8 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" ) @@ -80,6 +78,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 @@ -87,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 @@ -97,6 +101,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. @@ -107,16 +114,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. @@ -131,12 +137,9 @@ 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. - 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 @@ -171,13 +174,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 { @@ -278,52 +278,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 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. -// 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. @@ -353,6 +316,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 @@ -409,11 +375,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, } 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/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. 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/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..82ae9515c 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) @@ -283,12 +283,12 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall 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 } // 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..a18eb0c9a 100644 --- a/pkg/sentry/syscalls/syscalls.go +++ b/pkg/sentry/syscalls/syscalls.go @@ -77,7 +77,8 @@ 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) { + kernel.IncrementUnimplementedSyscallCounter(sysno) return 0, nil, err }, SupportLevel: kernel.SupportUnimplemented, @@ -94,8 +95,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 +114,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, 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 } diff --git a/pkg/tcpip/stack/gro.go b/pkg/tcpip/stack/gro.go index b403c05d4..acbb8f297 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,10 +222,12 @@ 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() + 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(groPkt *groPacket, flushGROPkt bool, pkt PacketBuffer } 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(groPkt *groPacket, flushGROPkt bool, pkt PacketBuffer // 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 } @@ -294,6 +295,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 +347,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 +378,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 } @@ -452,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 } @@ -503,7 +502,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 +600,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 +619,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 +648,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 +665,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] 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/boot/filter/config.go b/runsc/boot/filter/config.go index 85fde7ddf..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{ @@ -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 c93953b8d..6afc87404 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -101,6 +101,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 @@ -186,6 +189,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()) @@ -211,6 +229,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 @@ -287,6 +308,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) @@ -529,6 +556,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() } @@ -571,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(), } @@ -825,7 +852,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) } @@ -1391,7 +1418,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)) } @@ -1401,6 +1428,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/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/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/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/cmd/BUILD b/runsc/cmd/BUILD index bf2011b87..98ece4191 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -19,6 +19,7 @@ go_library( "do.go", "events.go", "exec.go", + "fd_mapping.go", "gofer.go", "help.go", "install.go", @@ -52,6 +53,7 @@ go_library( deps = [ "//pkg/abi/linux", "//pkg/atomicbitops", + "//pkg/cleanup", "//pkg/coretag", "//pkg/coverage", "//pkg/cpuid", @@ -60,7 +62,6 @@ go_library( "//pkg/prometheus", "//pkg/ring0", "//pkg/sentry/control", - "//pkg/sentry/fsutil/chdir", "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/platform", @@ -69,7 +70,6 @@ go_library( "//pkg/state/statefile", "//pkg/sync", "//pkg/unet", - "//pkg/urpc", "//runsc/boot", "//runsc/cmd/util", "//runsc/config", @@ -112,7 +112,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 6dfdff5b4..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" @@ -87,6 +86,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 @@ -169,6 +171,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, "custom-fds", "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") @@ -358,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 { @@ -392,6 +389,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..6d32edf98 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 intFlags } // 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 descriptors passed to the container. Can be supplied multiple times.") } // Execute implements subcommands.Command.Execute. It starts a process in an @@ -140,6 +144,35 @@ 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. + var files []*os.File + for _, fd := range ex.passFDs { + file := os.NewFile(uintptr(fd), "") + if file == nil { + util.Fatalf("failed to create file from file descriptor %d", fd) + } + fdMap[int(file.Fd())] = file + } + + // Close the underlying file descriptors after we have passed them. + defer func() { + for _, file := range files { + 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 +363,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 +416,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..48a76f63e --- /dev/null +++ b/runsc/cmd/fd_mapping.go @@ -0,0 +1,72 @@ +// 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 an 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 { + return fmt.Errorf("invalid flag value: must be of format M:N") + } + + 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/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) } 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))) 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 } diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index bbeb610b9..48eaf9f00 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 intFlags } // 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 descriptors passed to the container. 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 _, fd := range r.passFDs { + file := os.NewFile(uintptr(fd), "") + if file == nil { + return util.Errorf("Failed to create file from file descriptor %d", fd) + } + fdMap[fd] = 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/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 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..f1981e3b9 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. @@ -390,14 +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{ - "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, } } diff --git a/runsc/container/BUILD b/runsc/container/BUILD index 8788651b9..673ddf116 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -83,7 +83,6 @@ go_test( "//pkg/sync", "//pkg/test/testutil", "//pkg/unet", - "//pkg/urpc", "//runsc/boot", "//runsc/cgroup", "//runsc/config", 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 304c14347..a82f2255b 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 @@ -296,6 +300,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 f940ca272..26cc3186f 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() @@ -853,9 +854,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) { @@ -2726,3 +2727,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..cdf0340d5 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("--custom-fds=%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/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/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/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/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index bc41813ce..dbb5689d0 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 } @@ -929,8 +943,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) 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 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) } 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 { 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 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( 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; 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 diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 050ac3e60..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)); @@ -2259,7 +2308,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) { diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc index 32d9ed205..8816af529 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 ? 10000 : 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 @@ -796,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))); 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 { 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 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/**",